diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 6ced5d9..50c7890 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -44,6 +44,7 @@ jobs: app/build/outputs/apk/debug app/build/outputs/apk/release app/build/outputs/bundle/release + app/build/outputs/mapping/release validation/native-packaging.json app/build/generated/brand-fonts.json ui: diff --git a/api/README.md b/api/README.md index 141e9e3..7118baa 100644 --- a/api/README.md +++ b/api/README.md @@ -1,6 +1,6 @@ # API contract provenance -`openapi.json` is an unchanged, owner-supplied OpenAPI 3.0.3 snapshot from `SpaceWorks-HQ/SpaceWorks`, `frontend/openapi-schema.json`, source commit `e7c2803a4a57870e11dc4faf9adc40ee54401632` (2026-10-16). SHA-256: `d5ec904eb2a52ca948ee38dae7840c8859a43e034a22f3b8dc478fd8851cbb9b` (4,063,303 bytes; 635 paths). +`openapi.json` is an unchanged, owner-supplied OpenAPI 3.0.3 snapshot from `SpaceWorks-HQ/SpaceWorks`, `frontend/openapi-schema.json`, branch `feature/mobile-backend` at commit `f65ba332` (schema last regenerated in `3123173d`, 2026-10-17). SHA-256: `962e6a4b5b6927db0214c11e4b3b5ff688f660dccb8e674a7403b996cb1d8460` (4,997,776 bytes; 760 paths; 1,436 schemas). It supersedes the 2026-10-16 snapshot `d5ec904e…` (635 paths) and publishes the backend 1a–1d mobile contracts: native config/attestation, phone device delivery, `session_authority`, vendor-discriminated payment intents, account deletion/reauthentication, logout-all and the push/link association routes. The backend's companion prose for rules the schema cannot express is its `docs/mobile/MOBILE-APP-PRD.md` (v2) and `docs/mobile-integration.md`; `docs/PRD.md` here remains the byte-identical v1 owner copy. `./gradlew :app:openApiGenerate` runs OpenAPI Generator 7.26.0 (`kotlin`, `jvm-retrofit2`, `kotlinx_serialization`, coroutines). `scripts/contract.py` mechanically selects implemented operation paths and their unchanged transitive schema references, avoiding unused API surface. The full authoritative snapshot remains committed. Expand the operation allowlist as each validated phase needs it. All client code lives in ignored build outputs; no handwritten request/response models. diff --git a/api/openapi.json b/api/openapi.json index 587703c..c1df20f 100644 --- a/api/openapi.json +++ b/api/openapi.json @@ -6,6 +6,138 @@ "description": "Multi-tenant makerspace hardware loan system.\n\nPublic flow: browse inventory, search with `q`, page with `page`, sign in as a member, submit a borrow request, then track it by public token.\n\nAdmin flow: authenticate with JWT, manage makerspaces, inventory, staff, QR labels, bulk imports, request review, issue, and return.\n\nAuthentication: staff/admin endpoints use `Authorization: Bearer `. Public browser endpoints can use `X-Publishable-Key` when public key hardening is enabled. Server API clients send `X-Client-Id`, `X-Timestamp`, `X-Nonce`, and `X-Signature`. Generate a unique, unpredictable `X-Nonce` for every request and sign the byte sequence `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY` with HMAC-SHA256. The nonce uses 1-128 characters from `A-Z`, `a-z`, `0-9`, `.`, `_`, `~`, and `-`. A deployment may temporarily accept the legacy nonce-less signed format while `APICLIENT_REQUIRE_NONCE` is disabled." }, "paths": { + "/.well-known/apple-app-site-association": { + "get": { + "operationId": ".well_known_apple_app_site_association_retrieve", + "tags": [ + "Mobile associations" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "applinks": { + "type": "object", + "properties": { + "apps": { + "type": "array", + "items": { + "type": "string" + } + }, + "details": { + "type": "array", + "items": { + "type": "object", + "properties": { + "appIDs": { + "type": "array", + "items": { + "type": "string" + } + }, + "components": { + "type": "array", + "items": { + "type": "object", + "properties": { + "/": { + "type": "string" + }, + "exclude": { + "type": "boolean" + } + } + } + } + } + } + } + } + }, + "webcredentials": { + "type": "object", + "properties": { + "apps": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, + "description": "" + }, + "400": { + "description": "Managed host middleware refuses the host." + }, + "404": { + "description": "Host is not an eligible frontend host." + } + } + } + }, + "/.well-known/assetlinks.json": { + "get": { + "operationId": ".well_known_assetlinks.json_retrieve", + "tags": [ + "Mobile associations" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "relation": { + "type": "array", + "items": { + "type": "string" + } + }, + "target": { + "type": "object", + "properties": { + "namespace": { + "type": "string" + }, + "package_name": { + "type": "string" + }, + "sha256_cert_fingerprints": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "description": "" + }, + "400": { + "description": "Managed host middleware refuses the host." + }, + "404": { + "description": "Host is not an eligible frontend host." + } + } + } + }, "/api/public/{makerspace_slug}/inventory/": { "get": { "operationId": "api_public_inventory_list", @@ -772,131 +904,27 @@ } } }, - "/api/v1/admin/analytics/{report_key}": { + "/api/v1/admin/accessible-booking-spaces/": { "get": { - "operationId": "api_v1_admin_analytics_retrieve", - "summary": "Get aggregate analytics report", + "operationId": "api_v1_admin_accessible_booking_spaces_retrieve", "parameters": [ { "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", + "name": "makerspace_id", "schema": { "type": "integer" } }, { "in": "query", - "name": "months", + "name": "page", "schema": { "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "path", - "name": "report_key", - "schema": { - "type": "string", - "enum": [ - "active-loans", - "booking-utilization", - "cash-book", - "certification-coverage", - "communications-health", - "community-engagement", - "damaged-lost", - "damaged-missing", - "event-attendance", - "evidence-compliance", - "fablab-health", - "import-quality", - "inventory-control", - "loan-throughput", - "machine-service", - "machine-usage", - "maintenance-activity", - "member-activity", - "member-retention", - "membership-revenue", - "module-operational-health", - "most-lent", - "payment-cash-book", - "payment-reconciliation", - "printer-service", - "procurement-performance", - "qr-scans", - "recently-added", - "returns", - "revenue-over-time", - "summary", - "taken-items", - "top-borrowers" - ] - }, - "required": true - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] } } ], "tags": [ - "Analytics" + "Admin bookings" ], "security": [ { @@ -908,7 +936,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/BookableSpaceListResponse" } } }, @@ -918,61 +946,73 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "" } } } }, - "/api/v1/admin/api-clients/{id}": { + "/api/v1/admin/accessible-events/": { "get": { - "operationId": "api_v1_admin_api_clients_retrieve", - "summary": "Retrieve, update, or delete API client", + "operationId": "api_v1_admin_accessible_events_retrieve", "parameters": [ { - "in": "path", - "name": "id", + "in": "query", + "name": "makerspace_id", "schema": { "type": "integer" - }, - "required": true + } + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "page_size", + "schema": { + "type": "integer" + } } ], "tags": [ - "API clients" + "Admin events" ], "security": [ { @@ -984,7 +1024,313 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApiClient" + "$ref": "#/components/schemas/EventListResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/admin/analytics/{report_key}": { + "get": { + "operationId": "api_v1_admin_analytics_retrieve", + "summary": "Get aggregate analytics report", + "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "path", + "name": "report_key", + "schema": { + "type": "string", + "enum": [ + "asset-usage", + "audit-activity", + "booking-lead-time", + "booking-occupancy", + "booking-utilization", + "cash-book", + "certification-coverage", + "communications-health", + "community-engagement", + "consumable-pools", + "consumable-usage", + "damaged-lost", + "damaged-missing", + "device-health", + "event-attendance", + "event-checkin-times", + "event-feedback-scores", + "event-funnel", + "event-revenue", + "event-signups", + "evidence-compliance", + "fablab-health", + "import-quality", + "inventory-control", + "inventory-utilisation", + "inventory-watch-demand", + "loan-throughput", + "machine-downtime", + "machine-errors", + "machine-service", + "machine-usage", + "machine-usage-heatmap", + "maintenance-activity", + "member-activity", + "member-retention", + "membership-growth", + "membership-revenue", + "mobile-auth", + "mobile-devices", + "module-operational-health", + "most-lent", + "organization-membership", + "payment-cash-book", + "payment-reconciliation", + "payments-ageing", + "platform-services", + "platform-usage", + "presence-activity", + "printer-jobs", + "printer-service", + "problem-reports", + "procurement-performance", + "qr-scans", + "recently-added", + "request-funnel", + "revenue-over-time", + "service-queue-times", + "stock-health", + "stock-transfers", + "stocktake-variance", + "summary", + "taken-items", + "warranty-expiry" + ] + }, + "required": true + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } + } + ], + "tags": [ + "Analytics" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AnalyticsReportResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid report request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." + } + } + } + }, + "/api/v1/admin/api-clients/{id}": { + "get": { + "operationId": "api_v1_admin_api_clients_retrieve", + "summary": "Retrieve, update, or delete API client", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "API clients" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiClient" } } }, @@ -1880,10 +2226,10 @@ } } }, - "/api/v1/admin/bookings/{id}/approve/": { - "post": { - "operationId": "api_v1_admin_bookings_approve_create", - "summary": "Approve a pending booking", + "/api/v1/admin/booking-staff-grants/{id}/": { + "patch": { + "operationId": "api_v1_admin_booking_staff_grants_partial_update", + "description": "Requires live tenant MANAGE_BOOKINGS and bookings.staff_grants. Resource grantees cannot delegate. Invitations expire after seven days; sender authority and permission subset are rechecked on redemption.", "parameters": [ { "in": "path", @@ -1897,6 +2243,25 @@ "tags": [ "Admin bookings" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedBookingStaffGrantPermissions" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedBookingStaffGrantPermissions" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedBookingStaffGrantPermissions" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -1907,13 +2272,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookingAdmin" + "$ref": "#/components/schemas/BookingStaffGrant" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -1921,9 +2286,9 @@ } } }, - "description": "Invalid request." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -1931,9 +2296,9 @@ } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -1941,9 +2306,9 @@ } } }, - "description": "Permission denied." + "description": "" }, - "404": { + "409": { "content": { "application/json": { "schema": { @@ -1951,9 +2316,9 @@ } } }, - "description": "Not found." + "description": "" }, - "409": { + "423": { "content": { "application/json": { "schema": { @@ -1961,15 +2326,46 @@ } } }, - "description": "Workflow conflict." + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Field validation, module or feature disabled." } } } }, - "/api/v1/admin/bookings/{id}/cancel/": { + "/api/v1/admin/booking-staff-grants/{id}/revoke/": { "post": { - "operationId": "api_v1_admin_bookings_cancel_create", - "summary": "Cancel a booking", + "operationId": "api_v1_admin_booking_staff_grants_revoke_create", + "description": "Requires live tenant MANAGE_BOOKINGS and bookings.staff_grants. Resource grantees cannot delegate. Invitations expire after seven days; sender authority and permission subset are rechecked on redemption.", "parameters": [ { "in": "path", @@ -1993,13 +2389,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookingAdmin" + "$ref": "#/components/schemas/BookingStaffGrant" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -2007,9 +2403,9 @@ } } }, - "description": "Invalid request." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -2017,9 +2413,9 @@ } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -2027,9 +2423,9 @@ } } }, - "description": "Permission denied." + "description": "" }, - "404": { + "409": { "content": { "application/json": { "schema": { @@ -2037,9 +2433,9 @@ } } }, - "description": "Not found." + "description": "" }, - "409": { + "423": { "content": { "application/json": { "schema": { @@ -2047,15 +2443,46 @@ } } }, - "description": "Workflow conflict." + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Field validation, module or feature disabled." } } } }, - "/api/v1/admin/bookings/{id}/complete/": { + "/api/v1/admin/booking-staff-invites/{id}/revoke/": { "post": { - "operationId": "api_v1_admin_bookings_complete_create", - "summary": "Complete a booking", + "operationId": "api_v1_admin_booking_staff_invites_revoke_create", + "description": "Requires live tenant MANAGE_BOOKINGS and bookings.staff_grants. Resource grantees cannot delegate. Invitations expire after seven days; sender authority and permission subset are rechecked on redemption.", "parameters": [ { "in": "path", @@ -2079,13 +2506,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookingAdmin" + "$ref": "#/components/schemas/BookingStaffGrantInvite" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -2093,7 +2520,124 @@ } } }, - "description": "Invalid request." + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Field validation, module or feature disabled." + } + } + } + }, + "/api/v1/admin/booking-staff-invites/redeem/": { + "post": { + "operationId": "api_v1_admin_booking_staff_invites_redeem_create", + "description": "Authenticated active account; single-use invite with verified email binding, current sender authority, selected tenant/origin, expiry and revocation checks. Rate limited to 10/min/account; private/no-store.", + "tags": [ + "Bookings" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingStaffGrantRedeem" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BookingStaffGrantRedeem" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BookingStaffGrantRedeem" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingStaffGrant" + } + } + }, + "description": "" }, "401": { "content": { @@ -2103,7 +2647,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -2113,7 +2657,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -2123,7 +2667,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -2133,15 +2677,56 @@ } } }, - "description": "Workflow conflict." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Field validation, module or feature disabled." } } } }, - "/api/v1/admin/bookings/{id}/no-show/": { + "/api/v1/admin/bookings/{id}/approve/": { "post": { - "operationId": "api_v1_admin_bookings_no_show_create", - "summary": "Mark a booking as no-show", + "operationId": "api_v1_admin_bookings_approve_create", + "summary": "Approve a pending booking", "parameters": [ { "in": "path", @@ -2224,10 +2809,10 @@ } } }, - "/api/v1/admin/bookings/{id}/reject/": { + "/api/v1/admin/bookings/{id}/cancel/": { "post": { - "operationId": "api_v1_admin_bookings_reject_create", - "summary": "Reject a pending booking", + "operationId": "api_v1_admin_bookings_cancel_create", + "summary": "Cancel a booking", "parameters": [ { "in": "path", @@ -2310,10 +2895,10 @@ } } }, - "/api/v1/admin/categories/{id}": { - "get": { - "operationId": "api_v1_admin_categories_retrieve", - "summary": "Retrieve, update, or delete inventory category", + "/api/v1/admin/bookings/{id}/complete/": { + "post": { + "operationId": "api_v1_admin_bookings_complete_create", + "summary": "Complete a booking", "parameters": [ { "in": "path", @@ -2325,7 +2910,7 @@ } ], "tags": [ - "Admin inventory" + "Admin bookings" ], "security": [ { @@ -2337,99 +2922,70 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CategoryAdmin" + "$ref": "#/components/schemas/BookingAdmin" } } }, "description": "" - } - } - }, - "patch": { - "operationId": "api_v1_admin_categories_partial_update", - "summary": "Retrieve, update, or delete inventory category", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin inventory" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedCategoryAdmin" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedCategoryAdmin" + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedCategoryAdmin" + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + }, + "description": "Permission denied." + }, + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CategoryAdmin" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - } - } - }, - "delete": { - "operationId": "api_v1_admin_categories_destroy", - "summary": "Retrieve, update, or delete inventory category", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin inventory" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" + "description": "Workflow conflict." } } } }, - "/api/v1/admin/certification-grants/{id}/renew": { - "post": { - "operationId": "api_v1_admin_certification_grants_renew_create", - "summary": "Renew training with a new grant", + "/api/v1/admin/bookings/{id}/details/": { + "patch": { + "operationId": "api_v1_admin_bookings_details_partial_update", + "description": "Requires resource manage_reservations and bookings.staff_tools. Unknown fields are refused; answers use the current schema.", + "summary": "Edit private reservation note or answers", "parameters": [ { "in": "path", @@ -2441,23 +2997,23 @@ } ], "tags": [ - "Admin machines" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationGrantRevoke" + "$ref": "#/components/schemas/PatchedBookingDetailsInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CertificationGrantRevoke" + "$ref": "#/components/schemas/PatchedBookingDetailsInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CertificationGrantRevoke" + "$ref": "#/components/schemas/PatchedBookingDetailsInput" } } } @@ -2468,38 +3024,83 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationGrant" + "$ref": "#/components/schemas/BookingAdmin" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Too many requests." } } } }, - "/api/v1/admin/certification-grants/{id}/revoke": { + "/api/v1/admin/bookings/{id}/no-show/": { "post": { - "operationId": "api_v1_admin_certification_grants_revoke_create", - "summary": "Revoke a certification grant", + "operationId": "api_v1_admin_bookings_no_show_create", + "summary": "Mark a booking as no-show", "parameters": [ { "in": "path", @@ -2511,27 +3112,8 @@ } ], "tags": [ - "Admin machines" + "Admin bookings" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CertificationGrantRevoke" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CertificationGrantRevoke" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CertificationGrantRevoke" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -2542,153 +3124,69 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationGrant" + "$ref": "#/components/schemas/BookingAdmin" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace frozen for tenant migration." - } - } - } - }, - "/api/v1/admin/certification-types/{id}": { - "patch": { - "operationId": "api_v1_admin_certification_types_partial_update", - "summary": "Update a certification type", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedCertificationTypeUpdate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedCertificationTypeUpdate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedCertificationTypeUpdate" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationType" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "description": "Invalid request." }, "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace frozen for tenant migration." - } - } - }, - "delete": { - "operationId": "api_v1_admin_certification_types_destroy", - "summary": "Deactivate a certification type", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationType" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." }, - "423": { - "description": "Makerspace frozen for tenant migration." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." } } } }, - "/api/v1/admin/certification-types/{id}/grants": { - "get": { - "operationId": "api_v1_admin_certification_types_grants_retrieve", - "summary": "List grants for a certification type", + "/api/v1/admin/bookings/{id}/reject/": { + "post": { + "operationId": "api_v1_admin_bookings_reject_create", + "summary": "Reject a pending booking", "parameters": [ { "in": "path", @@ -2697,18 +3195,10 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - }, - "description": "Page number (24 rows per page)." } ], "tags": [ - "Admin machines" + "Admin bookings" ], "security": [ { @@ -2720,156 +3210,70 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationGrantPage" + "$ref": "#/components/schemas/BookingAdmin" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - }, - "post": { - "operationId": "api_v1_admin_certification_types_grants_create", - "summary": "Grant a certification to a membership", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CertificationGrantCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CertificationGrantCreate" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CertificationGrantCreate" - } - } + "description": "Invalid request." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationGrant" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace frozen for tenant migration." - } - } - } - }, - "/api/v1/admin/certification-types/{id}/members": { - "get": { - "operationId": "api_v1_admin_certification_types_members_retrieve", - "summary": "List active members eligible for training grants", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true + "description": "Not found." }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - }, - "description": "Page number (24 rows per page)." - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationMemberPage" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "description": "Workflow conflict." } } } }, - "/api/v1/admin/containers/{id}": { - "get": { - "operationId": "api_v1_admin_containers_retrieve", - "summary": "Retrieve container", + "/api/v1/admin/bookings/{id}/restore/": { + "post": { + "operationId": "api_v1_admin_bookings_restore_create", + "description": "Requires resource manage_reservations and bookings.staff_tools. Within five minutes, restore the exact pending/confirmed state under current admission. Any historical or newly required charge refuses undo.", + "summary": "Undo a recent cancellation without money movement", "parameters": [ { "in": "path", @@ -2881,7 +3285,7 @@ } ], "tags": [ - "Containers" + "Admin bookings" ], "security": [ { @@ -2893,7 +3297,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/BookingAdmin" } } }, @@ -2907,7 +3311,7 @@ } } }, - "description": "" + "description": "Invalid request." }, "401": { "content": { @@ -2917,7 +3321,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -2927,7 +3331,7 @@ } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { @@ -2937,13 +3341,36 @@ } } }, - "description": "" + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Too many requests." } } - }, - "patch": { - "operationId": "api_v1_admin_containers_partial_update", - "summary": "Update container", + } + }, + "/api/v1/admin/bookings/{id}/time/": { + "post": { + "operationId": "api_v1_admin_bookings_time_create", + "description": "Requires resource manage_reservations and bookings.staff_tools. Both old and proposed windows must be beyond the cutoff; ordinary admission rules apply. Identity, status and flat charge are preserved.", + "summary": "Move a pending or confirmed reservation", "parameters": [ { "in": "path", @@ -2955,26 +3382,27 @@ } ], "tags": [ - "Containers" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedBox" + "$ref": "#/components/schemas/BookingTimeInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedBox" + "$ref": "#/components/schemas/BookingTimeInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedBox" + "$ref": "#/components/schemas/BookingTimeInput" } } - } + }, + "required": true }, "security": [ { @@ -2986,7 +3414,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/BookingAdmin" } } }, @@ -3000,7 +3428,7 @@ } } }, - "description": "" + "description": "Invalid request." }, "401": { "content": { @@ -3010,7 +3438,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -3020,7 +3448,7 @@ } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { @@ -3030,15 +3458,35 @@ } } }, - "description": "" + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Too many requests." } } } }, - "/api/v1/admin/containers/{id}/contents": { + "/api/v1/admin/categories/{id}": { "get": { - "operationId": "api_v1_admin_containers_contents_retrieve", - "summary": "Get container contents", + "operationId": "api_v1_admin_categories_retrieve", + "summary": "Retrieve, update, or delete inventory category", "parameters": [ { "in": "path", @@ -3050,7 +3498,7 @@ } ], "tags": [ - "Containers" + "Admin inventory" ], "security": [ { @@ -3062,19 +3510,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContainerContents" + "$ref": "#/components/schemas/CategoryAdmin" } } }, "description": "" } } - } - }, - "/api/v1/admin/containers/{id}/history": { - "get": { - "operationId": "api_v1_admin_containers_history_retrieve", - "summary": "Get container scan history", + }, + "patch": { + "operationId": "api_v1_admin_categories_partial_update", + "summary": "Retrieve, update, or delete inventory category", "parameters": [ { "in": "path", @@ -3086,8 +3532,27 @@ } ], "tags": [ - "Containers" + "Admin inventory" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedCategoryAdmin" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedCategoryAdmin" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedCategoryAdmin" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -3098,19 +3563,46 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContainerHistory" + "$ref": "#/components/schemas/CategoryAdmin" } } }, "description": "" } } + }, + "delete": { + "operationId": "api_v1_admin_categories_destroy", + "summary": "Retrieve, update, or delete inventory category", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin inventory" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } } }, - "/api/v1/admin/containers/{id}/move": { + "/api/v1/admin/certification-grants/{id}/renew": { "post": { - "operationId": "api_v1_admin_containers_move_create", - "summary": "Move or update container", + "operationId": "api_v1_admin_certification_grants_renew_create", + "summary": "Renew training with a new grant", "parameters": [ { "in": "path", @@ -3122,23 +3614,23 @@ } ], "tags": [ - "Containers" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ContainerMove" + "$ref": "#/components/schemas/CertificationGrantRevoke" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ContainerMove" + "$ref": "#/components/schemas/CertificationGrantRevoke" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ContainerMove" + "$ref": "#/components/schemas/CertificationGrantRevoke" } } } @@ -3149,91 +3641,66 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/CertificationGrant" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." } } } }, - "/api/v1/admin/device-printer-bindings/{id}": { - "patch": { - "operationId": "api_v1_admin_device_printer_bindings_partial_update", + "/api/v1/admin/certification-grants/{id}/revoke": { + "post": { + "operationId": "api_v1_admin_certification_grants_revoke_create", + "summary": "Revoke a certification grant", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Devices" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedBindingUpdate" + "$ref": "#/components/schemas/CertificationGrantRevoke" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedBindingUpdate" + "$ref": "#/components/schemas/CertificationGrantRevoke" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedBindingUpdate" + "$ref": "#/components/schemas/CertificationGrantRevoke" } } } @@ -3248,99 +3715,114 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Binding" + "$ref": "#/components/schemas/CertificationGrant" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Invalid payload or adapter module disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Authentication/signature/replay refused." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Authority or device revoked." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace frozen for tenant migration." + } + } + } + }, + "/api/v1/admin/certification-types/{id}": { + "patch": { + "operationId": "api_v1_admin_certification_types_partial_update", + "summary": "Update a certification type", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machines" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedCertificationTypeUpdate" } }, - "description": "Target outside scope or absent." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedCertificationTypeUpdate" } }, - "description": "Conflicting command/report." - }, - "410": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedCertificationTypeUpdate" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/CertificationType" } } }, - "description": "Delivery lease expired." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Tenant frozen for migration." + "description": "Makerspace frozen for tenant migration." } } }, "delete": { - "operationId": "api_v1_admin_device_printer_bindings_destroy", + "operationId": "api_v1_admin_certification_types_destroy", + "summary": "Deactivate a certification type", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Devices" + "Admin machines" ], "security": [ { @@ -3348,99 +3830,58 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "400": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/CertificationType" } } }, - "description": "Invalid payload or adapter module disabled." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Authentication/signature/replay refused." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Authority or device revoked." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Target outside scope or absent." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Conflicting command/report." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Delivery lease expired." + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Tenant frozen for migration." + "description": "Makerspace frozen for tenant migration." } } } }, - "/api/v1/admin/devices/{id}": { + "/api/v1/admin/certification-types/{id}/grants": { "get": { - "operationId": "api_v1_admin_devices_retrieve", - "summary": "Retrieve a device", + "operationId": "api_v1_admin_certification_types_grants_retrieve", + "summary": "List grants for a certification type", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number (24 rows per page)." } ], "tags": [ - "Devices" + "Admin machines" ], "security": [ { @@ -3452,99 +3893,61 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Device" + "$ref": "#/components/schemas/CertificationGrantPage" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Invalid request or module disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Not found." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Tenant frozen for migration." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } }, - "patch": { - "operationId": "api_v1_admin_devices_partial_update", - "summary": "Rename a device or change its offline threshold", + "post": { + "operationId": "api_v1_admin_certification_types_grants_create", + "summary": "Grant a certification to a membership", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Devices" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedDeviceUpdate" + "$ref": "#/components/schemas/CertificationGrantCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedDeviceUpdate" + "$ref": "#/components/schemas/CertificationGrantCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedDeviceUpdate" + "$ref": "#/components/schemas/CertificationGrantCreate" } } - } + }, + "required": true }, "security": [ { @@ -3552,86 +3955,58 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Device" + "$ref": "#/components/schemas/CertificationGrant" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Invalid request or module disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Tenant frozen for migration." + "description": "Makerspace frozen for tenant migration." } } } }, - "/api/v1/admin/devices/{id}/enrollments": { - "post": { - "operationId": "api_v1_admin_devices_enrollments_create", - "summary": "Issue a new enrollment (enroll, rotate or reactivate by device status)", + "/api/v1/admin/certification-types/{id}/members": { + "get": { + "operationId": "api_v1_admin_certification_types_members_retrieve", + "summary": "List active members eligible for training grants", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number (24 rows per page)." } ], "tags": [ - "Devices" + "Admin machines" ], "security": [ { @@ -3639,86 +4014,47 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EnrollmentGrant" + "$ref": "#/components/schemas/CertificationMemberPage" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Invalid request or module disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Not found." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Tenant frozen for migration." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } } }, - "/api/v1/admin/devices/{id}/revoke": { - "post": { - "operationId": "api_v1_admin_devices_revoke_create", - "summary": "Revoke a device immediately", + "/api/v1/admin/containers/{id}": { + "get": { + "operationId": "api_v1_admin_containers_retrieve", + "summary": "Retrieve container", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Devices" + "Containers" ], "security": [ { @@ -3730,7 +4066,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Device" + "$ref": "#/components/schemas/Box" } } }, @@ -3740,59 +4076,47 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request or module disabled." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Not found." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "" } } - } - }, - "/api/v1/admin/direct-loans/{id}/return": { - "post": { - "operationId": "api_v1_admin_direct_loans_return_create", - "summary": "Return direct handout loan", + }, + "patch": { + "operationId": "api_v1_admin_containers_partial_update", + "summary": "Update container", "parameters": [ { "in": "path", @@ -3804,27 +4128,26 @@ } ], "tags": [ - "Admin requests" + "Containers" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DirectLoanReturn" + "$ref": "#/components/schemas/PatchedBox" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/DirectLoanReturn" + "$ref": "#/components/schemas/PatchedBox" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/DirectLoanReturn" + "$ref": "#/components/schemas/PatchedBox" } } - }, - "required": true + } }, "security": [ { @@ -3836,7 +4159,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DirectLoan" + "$ref": "#/components/schemas/Box" } } }, @@ -3850,7 +4173,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -3860,7 +4183,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -3870,7 +4193,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -3880,34 +4203,15 @@ } } }, - "description": "Not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Workflow conflict." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Service unavailable." + "description": "" } } } }, - "/api/v1/admin/event-certificates/{id}/download/": { + "/api/v1/admin/containers/{id}/contents": { "get": { - "operationId": "api_v1_admin_event_certificates_download_retrieve", + "operationId": "api_v1_admin_containers_contents_retrieve", + "summary": "Get container contents", "parameters": [ { "in": "path", @@ -3919,7 +4223,7 @@ } ], "tags": [ - "Admin events" + "Containers" ], "security": [ { @@ -3931,78 +4235,55 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificateDownload" + "$ref": "#/components/schemas/ContainerContents" } } }, "description": "" - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Certificate revoked." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Certificate storage unavailable." - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid request." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Event management is required." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + } + } + } + }, + "/api/v1/admin/containers/{id}/history": { + "get": { + "operationId": "api_v1_admin_containers_history_retrieve", + "summary": "Get container scan history", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" }, - "description": "Event resource not found." - }, - "409": { + "required": true + } + ], + "tags": [ + "Containers" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ContainerHistory" } } }, - "description": "Event state conflict." + "description": "" } } } }, - "/api/v1/admin/event-certificates/{id}/reissue/": { + "/api/v1/admin/containers/{id}/move": { "post": { - "operationId": "api_v1_admin_event_certificates_reissue_create", + "operationId": "api_v1_admin_containers_move_create", + "summary": "Move or update container", "parameters": [ { "in": "path", @@ -4014,19 +4295,38 @@ } ], "tags": [ - "Admin events" + "Containers" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContainerMove" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ContainerMove" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ContainerMove" + } + } + } + }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificateSummary" + "$ref": "#/components/schemas/Box" } } }, @@ -4040,9 +4340,9 @@ } } }, - "description": "Invalid request." + "description": "" }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -4050,9 +4350,9 @@ } } }, - "description": "Event management is required." + "description": "" }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -4060,9 +4360,9 @@ } } }, - "description": "Event resource not found." + "description": "" }, - "409": { + "404": { "content": { "application/json": { "schema": { @@ -4070,46 +4370,46 @@ } } }, - "description": "Event state conflict." + "description": "" } } } }, - "/api/v1/admin/event-certificates/{id}/revoke/": { - "post": { - "operationId": "api_v1_admin_event_certificates_revoke_create", + "/api/v1/admin/device-printer-bindings/{id}": { + "patch": { + "operationId": "api_v1_admin_device_printer_bindings_partial_update", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin events" + "Devices" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificateRevoke" + "$ref": "#/components/schemas/PatchedBindingUpdate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CertificateRevoke" + "$ref": "#/components/schemas/PatchedBindingUpdate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CertificateRevoke" + "$ref": "#/components/schemas/PatchedBindingUpdate" } } - }, - "required": true + } }, "security": [ { @@ -4121,7 +4421,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificateSummary" + "$ref": "#/components/schemas/Binding" } } }, @@ -4131,61 +4431,89 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid request." + "description": "Invalid payload or adapter module disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authentication/signature/replay refused." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event management is required." + "description": "Authority or device revoked." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event resource not found." + "description": "Target outside scope or absent." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event state conflict." + "description": "Conflicting command/report." + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Delivery lease expired." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } - } - }, - "/api/v1/admin/event-collaborations/{id}/remove/": { - "post": { - "operationId": "api_v1_admin_event_collaborations_remove_create", - "summary": "Remove an event collaborator", + }, + "delete": { + "operationId": "api_v1_admin_device_printer_bindings_destroy", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin events" + "Devices" ], "security": [ { @@ -4200,136 +4528,92 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid collaboration request." + "description": "Invalid payload or adapter module disabled." }, - "403": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event management access denied." + "description": "Authentication/signature/replay refused." }, - "404": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event collaboration not found." - } - } - } - }, - "/api/v1/admin/event-collaborations/{id}/respond/": { - "post": { - "operationId": "api_v1_admin_event_collaborations_respond_create", - "summary": "Accept or decline an event collaboration", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin events" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EventCollaborationRespond" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/EventCollaborationRespond" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/EventCollaborationRespond" - } - } + "description": "Authority or device revoked." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventCollaborator" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Target outside scope or absent." }, - "400": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid collaboration request." + "description": "Conflicting command/report." }, - "403": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event management access denied." + "description": "Delivery lease expired." }, - "404": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event collaboration not found." + "description": "Tenant frozen for migration." } } } }, - "/api/v1/admin/event-invites/{id}/revoke/": { - "post": { - "operationId": "api_v1_admin_event_invites_revoke_create", + "/api/v1/admin/devices/{id}": { + "get": { + "operationId": "api_v1_admin_devices_retrieve", + "summary": "Retrieve a device", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin events" + "Devices" ], "security": [ { @@ -4341,7 +4625,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Invite" + "$ref": "#/components/schemas/Device" } } }, @@ -4351,102 +4635,90 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid field values, disabled events module or disabled feature." + "description": "Invalid request or module disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Not found." }, "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Tenant frozen for migration." } } - } - }, - "/api/v1/admin/event-promos/{id}/revoke/": { - "post": { - "operationId": "api_v1_admin_event_promos_revoke_create", + }, + "patch": { + "operationId": "api_v1_admin_devices_partial_update", + "summary": "Rename a device or change its offline threshold", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin events" + "Devices" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedDeviceUpdate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedDeviceUpdate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedDeviceUpdate" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -4457,7 +4729,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Promo" + "$ref": "#/components/schemas/Device" } } }, @@ -4467,102 +4739,72 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid field values, disabled events module or disabled feature." + "description": "Invalid request or module disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Not found." }, "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Tenant frozen for migration." } } } }, - "/api/v1/admin/event-registrations/{id}/approve/": { + "/api/v1/admin/devices/{id}/enrollments": { "post": { - "operationId": "api_v1_admin_event_registrations_approve_create", - "summary": "Approve a pending event registration", + "operationId": "api_v1_admin_devices_enrollments_create", + "summary": "Issue a new enrollment (enroll, rotate or reactivate by device status)", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin events" + "Devices" ], "security": [ { @@ -4570,11 +4812,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventRegistrationAdmin" + "$ref": "#/components/schemas/EnrollmentGrant" } } }, @@ -4584,72 +4826,72 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Unexpected request body or events module disabled." + "description": "Invalid request or module disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Authentication is required." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event management is required." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Registration not found." + "description": "Not found." }, - "409": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event state or capacity conflict." + "description": "Tenant frozen for migration." } } } }, - "/api/v1/admin/event-registrations/{id}/cancel/": { + "/api/v1/admin/devices/{id}/revoke": { "post": { - "operationId": "api_v1_admin_event_registrations_cancel_create", - "summary": "Cancel an event registration and release its pending charge", + "operationId": "api_v1_admin_devices_revoke_create", + "summary": "Revoke a device immediately", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin events" + "Devices" ], "security": [ { @@ -4661,7 +4903,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventRegistrationAdmin" + "$ref": "#/components/schemas/Device" } } }, @@ -4671,59 +4913,59 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Unexpected request body or events module disabled." + "description": "Invalid request or module disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Authentication is required." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event management is required." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Registration not found." + "description": "Not found." }, - "409": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event state or capacity conflict." + "description": "Tenant frozen for migration." } } } }, - "/api/v1/admin/event-registrations/{id}/correct-attendance/": { + "/api/v1/admin/direct-loans/{id}/return": { "post": { - "operationId": "api_v1_admin_event_registrations_correct_attendance_create", + "operationId": "api_v1_admin_direct_loans_return_create", + "summary": "Return direct handout loan", "parameters": [ { "in": "path", @@ -4735,8 +4977,28 @@ } ], "tags": [ - "Admin events" + "Admin requests" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DirectLoanReturn" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DirectLoanReturn" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DirectLoanReturn" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -4747,7 +5009,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AttendanceCorrectionResponse" + "$ref": "#/components/schemas/DirectLoan" } } }, @@ -4763,6 +5025,16 @@ }, "description": "Invalid request." }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, "403": { "content": { "application/json": { @@ -4771,7 +5043,7 @@ } } }, - "description": "Event management is required." + "description": "Permission denied." }, "404": { "content": { @@ -4781,7 +5053,7 @@ } } }, - "description": "Event resource not found." + "description": "Not found." }, "409": { "content": { @@ -4791,15 +5063,25 @@ } } }, - "description": "Event state conflict." + "description": "Workflow conflict." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." } } } }, - "/api/v1/admin/event-registrations/{id}/mark-attended/": { - "post": { - "operationId": "api_v1_admin_event_registrations_mark_attended_create", - "summary": "Mark an event registration attended", + "/api/v1/admin/event-certificates/{id}/download/": { + "get": { + "operationId": "api_v1_admin_event_certificates_download_retrieve", + "description": "Private audited certificate download; configurable renders are bounded to 30/hour per account.", "parameters": [ { "in": "path", @@ -4813,25 +5095,6 @@ "tags": [ "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EventAttendanceMark" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/EventAttendanceMark" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/EventAttendanceMark" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -4842,24 +5105,23 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventRegistrationAdmin" + "$ref": "#/components/schemas/CertificateDownload" } } }, "description": "" }, - "400": { + "429": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected request body or events module disabled." + "description": "" }, - "401": { + "410": { "content": { "application/json": { "schema": { @@ -4867,7 +5129,27 @@ } } }, - "description": "Authentication is required." + "description": "Certificate revoked." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Certificate storage unavailable." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." }, "403": { "content": { @@ -4887,7 +5169,7 @@ } } }, - "description": "Registration not found." + "description": "Event resource not found." }, "409": { "content": { @@ -4897,15 +5179,14 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Event state conflict." } } } }, - "/api/v1/admin/event-registrations/{id}/promote/": { + "/api/v1/admin/event-certificates/{id}/reissue/": { "post": { - "operationId": "api_v1_admin_event_registrations_promote_create", - "summary": "Manually promote an approved waitlisted registration", + "operationId": "api_v1_admin_event_certificates_reissue_create", "parameters": [ { "in": "path", @@ -4925,28 +5206,17 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventRegistrationAdmin" + "$ref": "#/components/schemas/CertificateSummary" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Unexpected request body or events module disabled." - }, - "401": { "content": { "application/json": { "schema": { @@ -4954,7 +5224,7 @@ } } }, - "description": "Authentication is required." + "description": "Invalid request." }, "403": { "content": { @@ -4974,7 +5244,7 @@ } } }, - "description": "Registration not found." + "description": "Event resource not found." }, "409": { "content": { @@ -4984,15 +5254,14 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Event state conflict." } } } }, - "/api/v1/admin/event-registrations/{id}/reject/": { + "/api/v1/admin/event-certificates/{id}/revoke/": { "post": { - "operationId": "api_v1_admin_event_registrations_reject_create", - "summary": "Reject a pending or waitlisted event registration", + "operationId": "api_v1_admin_event_certificates_revoke_create", "parameters": [ { "in": "path", @@ -5006,6 +5275,26 @@ "tags": [ "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CertificateRevoke" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CertificateRevoke" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CertificateRevoke" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -5016,24 +5305,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventRegistrationAdmin" + "$ref": "#/components/schemas/CertificateSummary" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Unexpected request body or events module disabled." - }, - "401": { "content": { "application/json": { "schema": { @@ -5041,7 +5319,7 @@ } } }, - "description": "Authentication is required." + "description": "Invalid request." }, "403": { "content": { @@ -5061,7 +5339,7 @@ } } }, - "description": "Registration not found." + "description": "Event resource not found." }, "409": { "content": { @@ -5071,14 +5349,15 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Event state conflict." } } } }, - "/api/v1/admin/event-series-collaborations/{id}/remove/": { + "/api/v1/admin/event-collaborations/{id}/remove/": { "post": { - "operationId": "api_v1_admin_event_series_collaborations_remove_create", + "operationId": "api_v1_admin_event_collaborations_remove_create", + "summary": "Remove an event collaborator", "parameters": [ { "in": "path", @@ -5090,7 +5369,7 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], "security": [ { @@ -5111,7 +5390,7 @@ }, "description": "Invalid collaboration request." }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -5119,9 +5398,9 @@ } } }, - "description": "Authentication required." + "description": "Event management access denied." }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -5129,9 +5408,65 @@ } } }, - "description": "Event management access denied." + "description": "Event collaboration not found." + } + } + } + }, + "/api/v1/admin/event-collaborations/{id}/respond/": { + "post": { + "operationId": "api_v1_admin_event_collaborations_respond_create", + "summary": "Accept or decline an event collaboration", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin events" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventCollaborationRespond" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/EventCollaborationRespond" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/EventCollaborationRespond" + } + } }, - "404": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventCollaborator" + } + } + }, + "description": "" + }, + "400": { "content": { "application/json": { "schema": { @@ -5139,9 +5474,9 @@ } } }, - "description": "Series collaboration not found." + "description": "Invalid collaboration request." }, - "409": { + "403": { "content": { "application/json": { "schema": { @@ -5149,9 +5484,9 @@ } } }, - "description": "Collaboration state conflict." + "description": "Event management access denied." }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -5159,14 +5494,14 @@ } } }, - "description": "Rate limit exceeded." + "description": "Event collaboration not found." } } } }, - "/api/v1/admin/event-series-collaborations/{id}/respond/": { + "/api/v1/admin/event-invites/{id}/revoke/": { "post": { - "operationId": "api_v1_admin_event_series_collaborations_respond_create", + "operationId": "api_v1_admin_event_invites_revoke_create", "parameters": [ { "in": "path", @@ -5178,28 +5513,8 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SeriesCollaborationRespond" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SeriesCollaborationRespond" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SeriesCollaborationRespond" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -5210,7 +5525,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SeriesCollaborator" + "$ref": "#/components/schemas/Invite" } } }, @@ -5220,11 +5535,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid collaboration request." + "description": "Invalid field values, disabled events module or disabled feature." }, "401": { "content": { @@ -5234,7 +5550,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -5244,7 +5560,7 @@ } } }, - "description": "Event management access denied." + "description": "" }, "404": { "content": { @@ -5254,7 +5570,7 @@ } } }, - "description": "Series collaboration not found." + "description": "" }, "409": { "content": { @@ -5264,7 +5580,17 @@ } } }, - "description": "Collaboration state conflict." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "429": { "content": { @@ -5274,14 +5600,24 @@ } } }, - "description": "Rate limit exceeded." + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/event-series/{id}/": { - "get": { - "operationId": "api_v1_admin_event_series_retrieve", + "/api/v1/admin/event-promos/{id}/revoke/": { + "post": { + "operationId": "api_v1_admin_event_promos_revoke_create", "parameters": [ { "in": "path", @@ -5293,7 +5629,7 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], "security": [ { @@ -5305,7 +5641,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesDetail" + "$ref": "#/components/schemas/Promo" } } }, @@ -5315,11 +5651,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid recurring event series." + "description": "Invalid field values, disabled events module or disabled feature." }, "401": { "content": { @@ -5329,7 +5666,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -5339,7 +5676,7 @@ } } }, - "description": "Event management access denied." + "description": "" }, "404": { "content": { @@ -5349,7 +5686,7 @@ } } }, - "description": "Event series not found." + "description": "" }, "409": { "content": { @@ -5359,7 +5696,17 @@ } } }, - "description": "Series state conflict." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "429": { "content": { @@ -5369,12 +5716,25 @@ } } }, - "description": "Rate limit exceeded." + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } - }, + } + }, + "/api/v1/admin/event-registrations/{id}/answers/": { "patch": { - "operationId": "api_v1_admin_event_series_partial_update", + "operationId": "api_v1_admin_event_registrations_answers_partial_update", + "summary": "Edit validated custom registration answers", "parameters": [ { "in": "path", @@ -5386,23 +5746,23 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedEventSeriesWrite" + "$ref": "#/components/schemas/PatchedRegistrationAnswers" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedEventSeriesWrite" + "$ref": "#/components/schemas/PatchedRegistrationAnswers" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedEventSeriesWrite" + "$ref": "#/components/schemas/PatchedRegistrationAnswers" } } } @@ -5417,7 +5777,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesMutationResponse" + "$ref": "#/components/schemas/EventRegistrationAdmin" } } }, @@ -5427,11 +5787,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid recurring event series." + "description": "Unexpected request body or events module disabled." }, "401": { "content": { @@ -5441,7 +5802,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -5451,7 +5812,7 @@ } } }, - "description": "Event management access denied." + "description": "Event management is required." }, "404": { "content": { @@ -5461,7 +5822,7 @@ } } }, - "description": "Event series not found." + "description": "Registration not found." }, "409": { "content": { @@ -5471,24 +5832,15 @@ } } }, - "description": "Series state conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/event-series/{id}/cancel/": { + "/api/v1/admin/event-registrations/{id}/approve/": { "post": { - "operationId": "api_v1_admin_event_series_cancel_create", + "operationId": "api_v1_admin_event_registrations_approve_create", + "summary": "Approve a pending event registration", "parameters": [ { "in": "path", @@ -5500,7 +5852,7 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], "security": [ { @@ -5512,7 +5864,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesMutationResponse" + "$ref": "#/components/schemas/EventRegistrationAdmin" } } }, @@ -5522,11 +5874,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid recurring event series." + "description": "Unexpected request body or events module disabled." }, "401": { "content": { @@ -5536,7 +5889,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -5546,7 +5899,7 @@ } } }, - "description": "Event management access denied." + "description": "Event management is required." }, "404": { "content": { @@ -5556,7 +5909,7 @@ } } }, - "description": "Event series not found." + "description": "Registration not found." }, "409": { "content": { @@ -5566,24 +5919,15 @@ } } }, - "description": "Series state conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/event-series/{id}/collaborators/": { - "get": { - "operationId": "api_v1_admin_event_series_collaborators_list", + "/api/v1/admin/event-registrations/{id}/cancel/": { + "post": { + "operationId": "api_v1_admin_event_registrations_cancel_create", + "summary": "Cancel an event registration and release its pending charge", "parameters": [ { "in": "path", @@ -5595,7 +5939,7 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], "security": [ { @@ -5607,10 +5951,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SeriesCollaborator" - } + "$ref": "#/components/schemas/EventRegistrationAdmin" } } }, @@ -5620,11 +5961,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid collaboration request." + "description": "Unexpected request body or events module disabled." }, "401": { "content": { @@ -5634,7 +5976,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -5644,7 +5986,7 @@ } } }, - "description": "Event management access denied." + "description": "Event management is required." }, "404": { "content": { @@ -5654,7 +5996,7 @@ } } }, - "description": "Series collaboration not found." + "description": "Registration not found." }, "409": { "content": { @@ -5664,22 +6006,14 @@ } } }, - "description": "Collaboration state conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Event state or capacity conflict." } } - }, - "put": { - "operationId": "api_v1_admin_event_series_collaborators_update", + } + }, + "/api/v1/admin/event-registrations/{id}/correct-attendance/": { + "post": { + "operationId": "api_v1_admin_event_registrations_correct_attendance_create", "parameters": [ { "in": "path", @@ -5691,28 +6025,8 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SeriesCollaboratorReplace" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SeriesCollaboratorReplace" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SeriesCollaboratorReplace" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -5723,10 +6037,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SeriesCollaborator" - } + "$ref": "#/components/schemas/AttendanceCorrectionResponse" } } }, @@ -5740,17 +6051,7 @@ } } }, - "description": "Invalid collaboration request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Invalid request." }, "403": { "content": { @@ -5760,7 +6061,7 @@ } } }, - "description": "Event management access denied." + "description": "Event management is required." }, "404": { "content": { @@ -5770,7 +6071,7 @@ } } }, - "description": "Series collaboration not found." + "description": "Event resource not found." }, "409": { "content": { @@ -5780,24 +6081,15 @@ } } }, - "description": "Collaboration state conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Event state conflict." } } } }, - "/api/v1/admin/event-series/{id}/complete/": { + "/api/v1/admin/event-registrations/{id}/mark-attended/": { "post": { - "operationId": "api_v1_admin_event_series_complete_create", + "operationId": "api_v1_admin_event_registrations_mark_attended_create", + "summary": "Mark an event registration attended", "parameters": [ { "in": "path", @@ -5809,8 +6101,27 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventAttendanceMark" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/EventAttendanceMark" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/EventAttendanceMark" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -5821,7 +6132,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesMutationResponse" + "$ref": "#/components/schemas/EventAttendanceResult" } } }, @@ -5831,11 +6142,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid recurring event series." + "description": "Unexpected request body or events module disabled." }, "401": { "content": { @@ -5845,7 +6157,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -5855,7 +6167,7 @@ } } }, - "description": "Event management access denied." + "description": "Event management is required." }, "404": { "content": { @@ -5865,7 +6177,7 @@ } } }, - "description": "Event series not found." + "description": "Registration not found." }, "409": { "content": { @@ -5875,24 +6187,15 @@ } } }, - "description": "Series state conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/event-series/{id}/extend/": { + "/api/v1/admin/event-registrations/{id}/promote/": { "post": { - "operationId": "api_v1_admin_event_series_extend_create", + "operationId": "api_v1_admin_event_registrations_promote_create", + "summary": "Manually promote an approved waitlisted registration", "parameters": [ { "in": "path", @@ -5904,7 +6207,7 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], "security": [ { @@ -5916,7 +6219,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesMutationResponse" + "$ref": "#/components/schemas/EventRegistrationAdmin" } } }, @@ -5926,11 +6229,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid recurring event series." + "description": "Unexpected request body or events module disabled." }, "401": { "content": { @@ -5940,7 +6244,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -5950,7 +6254,7 @@ } } }, - "description": "Event management access denied." + "description": "Event management is required." }, "404": { "content": { @@ -5960,7 +6264,7 @@ } } }, - "description": "Event series not found." + "description": "Registration not found." }, "409": { "content": { @@ -5970,25 +6274,15 @@ } } }, - "description": "Series state conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/event-series/{id}/image": { + "/api/v1/admin/event-registrations/{id}/reject/": { "post": { - "operationId": "api_v1_admin_event_series_image_create", - "summary": "Create a series image upload URL", + "operationId": "api_v1_admin_event_registrations_reject_create", + "summary": "Reject a pending or waitlisted event registration", "parameters": [ { "in": "path", @@ -6000,139 +6294,82 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadResponse" + "$ref": "#/components/schemas/EventRegistrationAdmin" } } }, "description": "" }, "400": { - "description": "Invalid image upload request." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Unexpected request body or events module disabled." }, "401": { - "description": "Authentication required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required." }, "403": { - "description": "Event management access is required." - }, - "404": { - "description": "Event series not found." - }, - "429": { - "description": "Rate limit exceeded." - }, - "503": { - "description": "Public image storage is unavailable." - } - } - }, - "put": { - "operationId": "api_v1_admin_event_series_image_update", - "summary": "Attach an uploaded series image", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin event series" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "description": "Event management is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" - } - } + "description": "Registration not found." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesDetail" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - }, - "400": { - "description": "Invalid image upload request." - }, - "401": { - "description": "Authentication required." - }, - "403": { - "description": "Event management access is required." - }, - "404": { - "description": "Event series not found." - }, - "429": { - "description": "Rate limit exceeded." - }, - "503": { - "description": "Public image storage is unavailable." + "description": "Event state or capacity conflict." } } - }, - "delete": { - "operationId": "api_v1_admin_event_series_image_destroy", - "summary": "Clear a series image", + } + }, + "/api/v1/admin/event-registrations/{id}/restore/": { + "post": { + "operationId": "api_v1_admin_event_registrations_restore_create", + "summary": "Exactly restore a cancellation within five minutes", "parameters": [ { "in": "path", @@ -6144,7 +6381,7 @@ } ], "tags": [ - "Admin event series" + "Admin events" ], "security": [ { @@ -6156,36 +6393,69 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesDetail" + "$ref": "#/components/schemas/EventRegistrationAdmin" } } }, "description": "" }, "400": { - "description": "Invalid image upload request." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Unexpected request body or events module disabled." }, "401": { - "description": "Authentication required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required." }, "403": { - "description": "Event management access is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management is required." }, "404": { - "description": "Event series not found." - }, - "429": { - "description": "Rate limit exceeded." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Registration not found." }, - "503": { - "description": "Public image storage is unavailable." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/event-series/{id}/occurrences/": { - "get": { - "operationId": "api_v1_admin_event_series_occurrences_retrieve", + "/api/v1/admin/event-series-collaborations/{id}/remove/": { + "post": { + "operationId": "api_v1_admin_event_series_collaborations_remove_create", "parameters": [ { "in": "path", @@ -6205,15 +6475,8 @@ } ], "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EventListResponse" - } - } - }, - "description": "" + "204": { + "description": "No response body" }, "400": { "content": { @@ -6223,7 +6486,7 @@ } } }, - "description": "Invalid recurring event series." + "description": "Invalid collaboration request." }, "401": { "content": { @@ -6253,7 +6516,7 @@ } } }, - "description": "Event series not found." + "description": "Series collaboration not found." }, "409": { "content": { @@ -6263,7 +6526,7 @@ } } }, - "description": "Series state conflict." + "description": "Collaboration state conflict." }, "429": { "content": { @@ -6278,9 +6541,9 @@ } } }, - "/api/v1/admin/event-series/{id}/publish/": { + "/api/v1/admin/event-series-collaborations/{id}/respond/": { "post": { - "operationId": "api_v1_admin_event_series_publish_create", + "operationId": "api_v1_admin_event_series_collaborations_respond_create", "parameters": [ { "in": "path", @@ -6294,6 +6557,26 @@ "tags": [ "Admin event series" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SeriesCollaborationRespond" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/SeriesCollaborationRespond" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/SeriesCollaborationRespond" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -6304,7 +6587,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesMutationResponse" + "$ref": "#/components/schemas/SeriesCollaborator" } } }, @@ -6318,7 +6601,7 @@ } } }, - "description": "Invalid recurring event series." + "description": "Invalid collaboration request." }, "401": { "content": { @@ -6348,7 +6631,7 @@ } } }, - "description": "Event series not found." + "description": "Series collaboration not found." }, "409": { "content": { @@ -6358,7 +6641,7 @@ } } }, - "description": "Series state conflict." + "description": "Collaboration state conflict." }, "429": { "content": { @@ -6373,10 +6656,9 @@ } } }, - "/api/v1/admin/events/{id}/": { + "/api/v1/admin/event-series/{id}/": { "get": { - "operationId": "api_v1_admin_events_retrieve", - "summary": "Retrieve an event", + "operationId": "api_v1_admin_event_series_retrieve", "parameters": [ { "in": "path", @@ -6388,7 +6670,7 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "security": [ { @@ -6400,7 +6682,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventAdmin" + "$ref": "#/components/schemas/EventSeriesDetail" } } }, @@ -6414,7 +6696,7 @@ } } }, - "description": "" + "description": "Invalid recurring event series." }, "401": { "content": { @@ -6424,7 +6706,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -6434,7 +6716,7 @@ } } }, - "description": "" + "description": "Event management access denied." }, "404": { "content": { @@ -6444,13 +6726,32 @@ } } }, - "description": "" + "description": "Event series not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Series state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." } } }, "patch": { - "operationId": "api_v1_admin_events_partial_update", - "summary": "Update an event", + "operationId": "api_v1_admin_event_series_partial_update", "parameters": [ { "in": "path", @@ -6462,23 +6763,23 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedEventWrite" + "$ref": "#/components/schemas/PatchedEventSeriesWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedEventWrite" + "$ref": "#/components/schemas/PatchedEventSeriesWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedEventWrite" + "$ref": "#/components/schemas/PatchedEventSeriesWrite" } } } @@ -6493,7 +6794,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventAdmin" + "$ref": "#/components/schemas/EventSeriesMutationResponse" } } }, @@ -6503,14 +6804,13 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid event details." + "description": "Invalid recurring event series." }, - "409": { + "401": { "content": { "application/json": { "schema": { @@ -6518,9 +6818,9 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Authentication required." }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -6528,9 +6828,9 @@ } } }, - "description": "" + "description": "Event management access denied." }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -6538,9 +6838,9 @@ } } }, - "description": "" + "description": "Event series not found." }, - "404": { + "409": { "content": { "application/json": { "schema": { @@ -6548,14 +6848,24 @@ } } }, - "description": "" + "description": "Series state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." } } } }, - "/api/v1/admin/events/{id}/badge-template/": { - "get": { - "operationId": "api_v1_admin_events_badge_template_retrieve", + "/api/v1/admin/event-series/{id}/cancel/": { + "post": { + "operationId": "api_v1_admin_event_series_cancel_create", "parameters": [ { "in": "path", @@ -6567,7 +6877,7 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "security": [ { @@ -6579,12 +6889,32 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BadgeTemplate" + "$ref": "#/components/schemas/EventSeriesMutationResponse" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid recurring event series." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, "403": { "content": { "application/json": { @@ -6593,7 +6923,7 @@ } } }, - "description": "" + "description": "Event management access denied." }, "404": { "content": { @@ -6603,12 +6933,34 @@ } } }, - "description": "" - } - } - }, - "put": { - "operationId": "api_v1_admin_events_badge_template_update", + "description": "Event series not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Series state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/admin/event-series/{id}/collaborators/": { + "get": { + "operationId": "api_v1_admin_event_series_collaborators_list", "parameters": [ { "in": "path", @@ -6620,27 +6972,8 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BadgeTemplate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/BadgeTemplate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/BadgeTemplate" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -6651,7 +6984,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BadgeTemplate" + "type": "array", + "items": { + "$ref": "#/components/schemas/SeriesCollaborator" + } } } }, @@ -6661,12 +6997,21 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid event details." + "description": "Invalid collaboration request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { "content": { @@ -6676,7 +7021,7 @@ } } }, - "description": "" + "description": "Event management access denied." }, "404": { "content": { @@ -6686,7 +7031,7 @@ } } }, - "description": "" + "description": "Series collaboration not found." }, "409": { "content": { @@ -6696,14 +7041,22 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Collaboration state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." } } - } - }, - "/api/v1/admin/events/{id}/badges.pdf": { - "post": { - "operationId": "api_v1_admin_events_badges.pdf_create", + }, + "put": { + "operationId": "api_v1_admin_event_series_collaborators_update", "parameters": [ { "in": "path", @@ -6715,23 +7068,23 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BadgePdfRequest" + "$ref": "#/components/schemas/SeriesCollaboratorReplace" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/BadgePdfRequest" + "$ref": "#/components/schemas/SeriesCollaboratorReplace" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/BadgePdfRequest" + "$ref": "#/components/schemas/SeriesCollaboratorReplace" } } }, @@ -6745,25 +7098,36 @@ "responses": { "200": { "content": { - "application/pdf": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "type": "array", + "items": { + "$ref": "#/components/schemas/SeriesCollaborator" + } } } }, - "description": "Print-ready badge PDF." + "description": "" }, "400": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid event details." + "description": "Invalid collaboration request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { "content": { @@ -6773,7 +7137,7 @@ } } }, - "description": "" + "description": "Event management access denied." }, "404": { "content": { @@ -6783,7 +7147,7 @@ } } }, - "description": "" + "description": "Series collaboration not found." }, "409": { "content": { @@ -6793,15 +7157,24 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Collaboration state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." } } } }, - "/api/v1/admin/events/{id}/cancel/": { + "/api/v1/admin/event-series/{id}/complete/": { "post": { - "operationId": "api_v1_admin_events_cancel_create", - "summary": "Cancel an event", + "operationId": "api_v1_admin_event_series_complete_create", "parameters": [ { "in": "path", @@ -6813,7 +7186,7 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "security": [ { @@ -6825,7 +7198,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventAdmin" + "$ref": "#/components/schemas/EventSeriesMutationResponse" } } }, @@ -6839,7 +7212,7 @@ } } }, - "description": "" + "description": "Invalid recurring event series." }, "401": { "content": { @@ -6849,7 +7222,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -6859,7 +7232,7 @@ } } }, - "description": "" + "description": "Event management access denied." }, "404": { "content": { @@ -6869,7 +7242,7 @@ } } }, - "description": "" + "description": "Event series not found." }, "409": { "content": { @@ -6879,15 +7252,24 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Series state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." } } } }, - "/api/v1/admin/events/{id}/check-in/offline-roster/": { - "get": { - "operationId": "api_v1_admin_events_check_in_offline_roster_retrieve", - "summary": "Download a minimal expiring offline check-in roster", + "/api/v1/admin/event-series/{id}/extend/": { + "post": { + "operationId": "api_v1_admin_event_series_extend_create", "parameters": [ { "in": "path", @@ -6899,7 +7281,7 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "security": [ { @@ -6911,7 +7293,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OfflineRosterResponse" + "$ref": "#/components/schemas/EventSeriesMutationResponse" } } }, @@ -6925,7 +7307,7 @@ } } }, - "description": "Feature disabled or invalid batch." + "description": "Invalid recurring event series." }, "401": { "content": { @@ -6935,7 +7317,7 @@ } } }, - "description": "Authentication or lease failed." + "description": "Authentication required." }, "403": { "content": { @@ -6945,7 +7327,7 @@ } } }, - "description": "Event authority changed." + "description": "Event management access denied." }, "404": { "content": { @@ -6955,7 +7337,7 @@ } } }, - "description": "Event not found." + "description": "Event series not found." }, "409": { "content": { @@ -6965,27 +7347,7 @@ } } }, - "description": "Roster window closed." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Synchronization deadline passed." - }, - "413": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Roster exceeds the offline limit." + "description": "Series state conflict." }, "429": { "content": { @@ -6995,15 +7357,15 @@ } } }, - "description": "Request rate limit exceeded." + "description": "Rate limit exceeded." } } } }, - "/api/v1/admin/events/{id}/check-in/offline-sync/": { + "/api/v1/admin/event-series/{id}/image": { "post": { - "operationId": "api_v1_admin_events_check_in_offline_sync_create", - "summary": "Synchronize queued offline event check-ins", + "operationId": "api_v1_admin_event_series_image_create", + "summary": "Create a series image upload URL", "parameters": [ { "in": "path", @@ -7015,23 +7377,23 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + "$ref": "#/components/schemas/PublicImageUploadRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + "$ref": "#/components/schemas/PublicImageUploadRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + "$ref": "#/components/schemas/PublicImageUploadRequest" } } }, @@ -7043,103 +7405,39 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OfflineCheckInSyncResponse" + "$ref": "#/components/schemas/PublicImageUploadResponse" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Feature disabled or invalid batch." + "description": "Invalid image upload request." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication or lease failed." + "description": "Authentication required." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Event authority changed." + "description": "Event management access is required." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Event not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Roster window closed." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Synchronization deadline passed." - }, - "413": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Roster exceeds the offline limit." + "description": "Event series not found." }, "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Request rate limit exceeded." + "description": "Rate limit exceeded." + }, + "503": { + "description": "Public image storage is unavailable." } } - } - }, - "/api/v1/admin/events/{id}/check-in/resolve/": { - "post": { - "operationId": "api_v1_admin_events_check_in_resolve_create", - "summary": "Resolve an event check-in token", + }, + "put": { + "operationId": "api_v1_admin_event_series_image_update", + "summary": "Attach an uploaded series image", "parameters": [ { "in": "path", @@ -7151,23 +7449,23 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventCheckInResolveRequest" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/EventCheckInResolveRequest" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/EventCheckInResolveRequest" + "$ref": "#/components/schemas/PublicImageAttachRequest" } } }, @@ -7183,49 +7481,88 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventCheckInResolveResponse" + "$ref": "#/components/schemas/EventSeriesDetail" } } }, "description": "" }, + "400": { + "description": "Invalid image upload request." + }, + "401": { + "description": "Authentication required." + }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Event access denied." + "description": "Event management access is required." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Registration not found." + "description": "Event series not found." }, "429": { + "description": "Rate limit exceeded." + }, + "503": { + "description": "Public image storage is unavailable." + } + } + }, + "delete": { + "operationId": "api_v1_admin_event_series_image_destroy", + "summary": "Clear a series image", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin event series" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/EventSeriesDetail" } } }, - "description": "Request rate limit exceeded." + "description": "" + }, + "400": { + "description": "Invalid image upload request." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "Event management access is required." + }, + "404": { + "description": "Event series not found." + }, + "429": { + "description": "Rate limit exceeded." + }, + "503": { + "description": "Public image storage is unavailable." } } } }, - "/api/v1/admin/events/{id}/check-in/station/": { + "/api/v1/admin/event-series/{id}/occurrences/": { "get": { - "operationId": "api_v1_admin_events_check_in_station_retrieve", - "summary": "Read venue-station configuration without revealing its PIN", + "operationId": "api_v1_admin_event_series_occurrences_retrieve", "parameters": [ { "in": "path", @@ -7237,7 +7574,7 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "security": [ { @@ -7249,7 +7586,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StationStatus" + "$ref": "#/components/schemas/EventListResponse" } } }, @@ -7263,9 +7600,9 @@ } } }, - "description": "Feature disabled or invalid request." + "description": "Invalid recurring event series." }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -7273,9 +7610,9 @@ } } }, - "description": "Event access or step-up denied." + "description": "Authentication required." }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -7283,9 +7620,9 @@ } } }, - "description": "Event not found." + "description": "Event management access denied." }, - "409": { + "404": { "content": { "application/json": { "schema": { @@ -7293,9 +7630,9 @@ } } }, - "description": "Rotate the PIN instead of revealing." + "description": "Event series not found." }, - "429": { + "409": { "content": { "application/json": { "schema": { @@ -7303,9 +7640,9 @@ } } }, - "description": "Request rate limit exceeded." + "description": "Series state conflict." }, - "503": { + "429": { "content": { "application/json": { "schema": { @@ -7313,13 +7650,14 @@ } } }, - "description": "Credential secrets are unavailable." + "description": "Rate limit exceeded." } } - }, - "delete": { - "operationId": "api_v1_admin_events_check_in_station_destroy", - "summary": "Disable a venue check-in station", + } + }, + "/api/v1/admin/event-series/{id}/publish/": { + "post": { + "operationId": "api_v1_admin_event_series_publish_create", "parameters": [ { "in": "path", @@ -7331,7 +7669,7 @@ } ], "tags": [ - "Admin events" + "Admin event series" ], "security": [ { @@ -7343,7 +7681,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StationStatus" + "$ref": "#/components/schemas/EventSeriesMutationResponse" } } }, @@ -7357,9 +7695,9 @@ } } }, - "description": "Feature disabled or invalid request." + "description": "Invalid recurring event series." }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -7367,9 +7705,9 @@ } } }, - "description": "Event access or step-up denied." + "description": "Authentication required." }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -7377,9 +7715,9 @@ } } }, - "description": "Event not found." + "description": "Event management access denied." }, - "409": { + "404": { "content": { "application/json": { "schema": { @@ -7387,9 +7725,9 @@ } } }, - "description": "Rotate the PIN instead of revealing." + "description": "Event series not found." }, - "429": { + "409": { "content": { "application/json": { "schema": { @@ -7397,9 +7735,9 @@ } } }, - "description": "Request rate limit exceeded." + "description": "Series state conflict." }, - "503": { + "429": { "content": { "application/json": { "schema": { @@ -7407,15 +7745,14 @@ } } }, - "description": "Credential secrets are unavailable." + "description": "Rate limit exceeded." } } } }, - "/api/v1/admin/events/{id}/check-in/station/reveal/": { - "post": { - "operationId": "api_v1_admin_events_check_in_station_reveal_create", - "summary": "Reveal the current station PIN after password step-up", + "/api/v1/admin/event-series/{id}/staff-grants/": { + "get": { + "operationId": "api_v1_admin_event_series_staff_grants_retrieve", "parameters": [ { "in": "path", @@ -7424,31 +7761,18 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } } ], "tags": [ "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/StationReveal" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/StationReveal" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/StationReveal" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -7459,7 +7783,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StationRevealResponse" + "$ref": "#/components/schemas/StaffGrantList" } } }, @@ -7473,9 +7797,9 @@ } } }, - "description": "Feature disabled or invalid request." + "description": "" }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -7483,9 +7807,9 @@ } } }, - "description": "Event access or step-up denied." + "description": "" }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -7493,7 +7817,17 @@ } } }, - "description": "Event not found." + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "409": { "content": { @@ -7503,7 +7837,17 @@ } } }, - "description": "Rotate the PIN instead of revealing." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "429": { "content": { @@ -7513,7 +7857,7 @@ } } }, - "description": "Request rate limit exceeded." + "description": "" }, "503": { "content": { @@ -7523,15 +7867,14 @@ } } }, - "description": "Credential secrets are unavailable." + "description": "" } } } }, - "/api/v1/admin/events/{id}/check-in/station/rotate/": { - "post": { - "operationId": "api_v1_admin_events_check_in_station_rotate_create", - "summary": "Create or rotate an event-scoped eight-digit station PIN", + "/api/v1/admin/event-series/{id}/staff-invites/": { + "get": { + "operationId": "api_v1_admin_event_series_staff_invites_retrieve", "parameters": [ { "in": "path", @@ -7540,6 +7883,13 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } } ], "tags": [ @@ -7555,7 +7905,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StationRotation" + "$ref": "#/components/schemas/StaffGrantInviteList" } } }, @@ -7569,7 +7919,17 @@ } } }, - "description": "Feature disabled or invalid request." + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { "content": { @@ -7579,7 +7939,7 @@ } } }, - "description": "Event access or step-up denied." + "description": "" }, "404": { "content": { @@ -7589,7 +7949,7 @@ } } }, - "description": "Event not found." + "description": "" }, "409": { "content": { @@ -7599,7 +7959,17 @@ } } }, - "description": "Rotate the PIN instead of revealing." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "429": { "content": { @@ -7609,7 +7979,7 @@ } } }, - "description": "Request rate limit exceeded." + "description": "" }, "503": { "content": { @@ -7619,15 +7989,12 @@ } } }, - "description": "Credential secrets are unavailable." + "description": "" } } - } - }, - "/api/v1/admin/events/{id}/collaborators/": { - "get": { - "operationId": "api_v1_admin_events_collaborators_list", - "summary": "List an event's collaborators", + }, + "post": { + "operationId": "api_v1_admin_event_series_staff_invites_create", "parameters": [ { "in": "path", @@ -7641,20 +8008,37 @@ "tags": [ "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaffGrantInviteInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StaffGrantInviteInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StaffGrantInviteInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventCollaborator" - } + "$ref": "#/components/schemas/StaffGrantInviteCreated" } } }, @@ -7668,9 +8052,9 @@ } } }, - "description": "Invalid collaboration request." + "description": "" }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -7678,9 +8062,9 @@ } } }, - "description": "Event management access denied." + "description": "" }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -7688,66 +8072,29 @@ } } }, - "description": "Event collaboration not found." - } - } - }, - "put": { - "operationId": "api_v1_admin_events_collaborators_update", - "summary": "Replace an event's collaborators", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin events" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EventCollaboratorReplace" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/EventCollaboratorReplace" + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/EventCollaboratorReplace" - } - } + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventCollaborator" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "423": { "content": { "application/json": { "schema": { @@ -7755,9 +8102,9 @@ } } }, - "description": "Invalid collaboration request." + "description": "" }, - "403": { + "429": { "content": { "application/json": { "schema": { @@ -7765,9 +8112,9 @@ } } }, - "description": "Event management access denied." + "description": "" }, - "404": { + "503": { "content": { "application/json": { "schema": { @@ -7775,15 +8122,14 @@ } } }, - "description": "Event collaboration not found." + "description": "" } } } }, - "/api/v1/admin/events/{id}/complete/": { - "post": { - "operationId": "api_v1_admin_events_complete_create", - "summary": "Complete an event", + "/api/v1/admin/event-series/{id}/website/": { + "get": { + "operationId": "api_v1_admin_event_series_website_retrieve", "parameters": [ { "in": "path", @@ -7795,7 +8141,7 @@ } ], "tags": [ - "Admin events" + "Event website" ], "security": [ { @@ -7807,7 +8153,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventAdmin" + "$ref": "#/components/schemas/Website" } } }, @@ -7821,7 +8167,7 @@ } } }, - "description": "" + "description": "Invalid content, stale revision, disabled feature or terminal event." }, "401": { "content": { @@ -7831,7 +8177,7 @@ } } }, - "description": "" + "description": "Sign in required." }, "403": { "content": { @@ -7841,7 +8187,7 @@ } } }, - "description": "" + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -7851,9 +8197,9 @@ } } }, - "description": "" + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -7861,16 +8207,22 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } - } - }, - "/api/v1/admin/events/{id}/eligible-members/": { - "get": { - "operationId": "api_v1_admin_events_eligible_members_list", - "description": "The roster the staff registration picker reads.\n\nHung off the EVENT rather than the makerspace, so it inherits `_manageable_event`\nand introduces no new authority question: whoever may manage this event may see who\nthey can register for it. A separate makerspace-level member list would have needed\nits own permission answer, and the obvious candidates were all wrong — the\ndirect-loan roster is gated on `ISSUE_DIRECT_LOAN` plus a self-checkout feature an\nevents manager need not hold, and the full membership list is `MANAGE_MAKERSPACE`.\n\nAlready-registered members are excluded: offering someone the picker can only reject\nas a duplicate is an error the interface should not have made available.", - "summary": "List members who can be registered for an event", + }, + "put": { + "operationId": "api_v1_admin_event_series_website_update", "parameters": [ { "in": "path", @@ -7882,8 +8234,28 @@ } ], "tags": [ - "Admin events" + "Event website" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteContentInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WebsiteContentInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WebsiteContentInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -7894,10 +8266,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventEligibleMember" - } + "$ref": "#/components/schemas/Website" } } }, @@ -7907,12 +8276,11 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected request body or events module disabled." + "description": "Invalid content, stale revision, disabled feature or terminal event." }, "401": { "content": { @@ -7922,7 +8290,7 @@ } } }, - "description": "Authentication is required." + "description": "Sign in required." }, "403": { "content": { @@ -7932,7 +8300,7 @@ } } }, - "description": "Event management is required." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -7942,9 +8310,9 @@ } } }, - "description": "Registration not found." + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -7952,14 +8320,24 @@ } } }, - "description": "Event state or capacity conflict." + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/events/{id}/feedback-responses/": { - "get": { - "operationId": "api_v1_admin_events_feedback_responses_retrieve", + "/api/v1/admin/event-series/{id}/website/assets/": { + "post": { + "operationId": "api_v1_admin_event_series_website_assets_create", "parameters": [ { "in": "path", @@ -7971,19 +8349,29 @@ } ], "tags": [ - "Admin events" + "Event website" ], + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WebsiteAssetInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackResponseList" + "$ref": "#/components/schemas/WebsiteAsset" } } }, @@ -7997,7 +8385,17 @@ } } }, - "description": "Invalid request." + "description": "Invalid content, stale revision, disabled feature or terminal event." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign in required." }, "403": { "content": { @@ -8007,7 +8405,7 @@ } } }, - "description": "Event management is required." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -8017,9 +8415,9 @@ } } }, - "description": "Event resource not found." + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -8027,15 +8425,33 @@ } } }, - "description": "Event state conflict." + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/events/{id}/feedback-survey/": { + "/api/v1/admin/event-series/{id}/website/assets/{asset_id}": { "get": { - "operationId": "api_v1_admin_events_feedback_survey_retrieve", + "operationId": "api_v1_admin_event_series_website_assets_retrieve", "parameters": [ + { + "in": "path", + "name": "asset_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "id", @@ -8046,7 +8462,7 @@ } ], "tags": [ - "Admin events" + "Event website" ], "security": [ { @@ -8058,11 +8474,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackSurveyAdminEnvelope" + "type": "string", + "format": "binary" } } }, - "description": "" + "description": "Private WebP image; no-store." }, "400": { "content": { @@ -8072,7 +8489,17 @@ } } }, - "description": "Invalid request." + "description": "Invalid content, stale revision, disabled feature or terminal event." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign in required." }, "403": { "content": { @@ -8082,7 +8509,7 @@ } } }, - "description": "Event management is required." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -8092,9 +8519,9 @@ } } }, - "description": "Event resource not found." + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -8102,13 +8529,31 @@ } } }, - "description": "Event state conflict." + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } }, - "put": { - "operationId": "api_v1_admin_events_feedback_survey_update", + "delete": { + "operationId": "api_v1_admin_event_series_website_assets_destroy", "parameters": [ + { + "in": "path", + "name": "asset_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "id", @@ -8119,45 +8564,28 @@ } ], "tags": [ - "Admin events" + "Event website" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FeedbackSurveyWrite" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/FeedbackSurveyWrite" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/FeedbackSurveyWrite" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackSurvey" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Invalid content, stale revision, disabled feature or terminal event." }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -8165,7 +8593,7 @@ } } }, - "description": "Invalid request." + "description": "Sign in required." }, "403": { "content": { @@ -8175,7 +8603,7 @@ } } }, - "description": "Event management is required." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -8185,9 +8613,9 @@ } } }, - "description": "Event resource not found." + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -8195,14 +8623,24 @@ } } }, - "description": "Event state conflict." + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/events/{id}/feedback-survey/close/": { + "/api/v1/admin/event-series/{id}/website/release/": { "post": { - "operationId": "api_v1_admin_events_feedback_survey_close_create", + "operationId": "api_v1_admin_event_series_website_release_create", "parameters": [ { "in": "path", @@ -8214,8 +8652,28 @@ } ], "tags": [ - "Admin events" + "Event website" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteReleaseInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WebsiteReleaseInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WebsiteReleaseInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -8226,7 +8684,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackSurvey" + "$ref": "#/components/schemas/Website" } } }, @@ -8240,7 +8698,17 @@ } } }, - "description": "Invalid request." + "description": "Invalid content, stale revision, disabled feature or terminal event." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign in required." }, "403": { "content": { @@ -8250,7 +8718,7 @@ } } }, - "description": "Event management is required." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -8260,9 +8728,9 @@ } } }, - "description": "Event resource not found." + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -8270,14 +8738,24 @@ } } }, - "description": "Event state conflict." + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/events/{id}/feedback-survey/open/": { + "/api/v1/admin/event-series/{id}/website/sessions/{slug}/delay/": { "post": { - "operationId": "api_v1_admin_events_feedback_survey_open_create", + "operationId": "api_v1_admin_event_series_website_sessions_delay_create", "parameters": [ { "in": "path", @@ -8286,11 +8764,39 @@ "type": "integer" }, "required": true + }, + { + "in": "path", + "name": "slug", + "schema": { + "type": "string" + }, + "required": true } ], "tags": [ - "Admin events" + "Event website" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteDelayInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WebsiteDelayInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WebsiteDelayInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -8301,7 +8807,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackSurvey" + "$ref": "#/components/schemas/Website" } } }, @@ -8315,7 +8821,17 @@ } } }, - "description": "Invalid request." + "description": "Invalid content, stale revision, disabled feature or terminal event." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign in required." }, "403": { "content": { @@ -8325,7 +8841,7 @@ } } }, - "description": "Event management is required." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -8335,9 +8851,9 @@ } } }, - "description": "Event resource not found." + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -8345,15 +8861,24 @@ } } }, - "description": "Event state conflict." + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/events/{id}/image": { - "post": { - "operationId": "api_v1_admin_events_image_create", - "summary": "Create an event image upload URL", + "/api/v1/admin/event-staff-grants/{id}/": { + "patch": { + "operationId": "api_v1_admin_event_staff_grants_partial_update", "parameters": [ { "in": "path", @@ -8371,21 +8896,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "$ref": "#/components/schemas/PatchedStaffGrantPermissions" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "$ref": "#/components/schemas/PatchedStaffGrantPermissions" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "$ref": "#/components/schemas/PatchedStaffGrantPermissions" } } - }, - "required": true + } }, "security": [ { @@ -8393,140 +8917,102 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadResponse" + "$ref": "#/components/schemas/StaffGrant" } } }, "description": "" }, "400": { - "description": "Invalid image upload request." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Event management access is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Event not found." - }, - "503": { - "description": "Public image storage is unavailable." - } - } - }, - "put": { - "operationId": "api_v1_admin_events_image_update", - "summary": "Attach an uploaded image to an event", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin events" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" - } - } + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventAdmin" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Invalid image object key or size." - }, - "403": { - "description": "Event management access is required." - }, - "404": { - "description": "Event not found." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "503": { - "description": "Public image storage is unavailable." - } - } - }, - "delete": { - "operationId": "api_v1_admin_events_image_destroy", - "summary": "Clear an event image", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin events" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventAdmin" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - }, - "403": { - "description": "Event management access is required." - }, - "404": { - "description": "Event not found." } } } }, - "/api/v1/admin/events/{id}/invites/": { - "get": { - "operationId": "api_v1_admin_events_invites_retrieve", + "/api/v1/admin/event-staff-grants/{id}/revoke/": { + "post": { + "operationId": "api_v1_admin_event_staff_grants_revoke_create", "parameters": [ { "in": "path", @@ -8535,13 +9021,6 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } } ], "tags": [ @@ -8557,7 +9036,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InviteList" + "$ref": "#/components/schemas/StaffGrant" } } }, @@ -8567,12 +9046,11 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid field values, disabled events module or disabled feature." + "description": "" }, "401": { "content": { @@ -8645,9 +9123,11 @@ "description": "" } } - }, + } + }, + "/api/v1/admin/event-staff-invites/{id}/revoke/": { "post": { - "operationId": "api_v1_admin_events_invites_create", + "operationId": "api_v1_admin_event_staff_invites_revoke_create", "parameters": [ { "in": "path", @@ -8661,36 +9141,17 @@ "tags": [ "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InviteInput" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/InviteInput" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/InviteInput" - } - } - } - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Invite" + "$ref": "#/components/schemas/StaffGrantInvite" } } }, @@ -8700,12 +9161,11 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid field values, disabled events module or disabled feature." + "description": "" }, "401": { "content": { @@ -8780,38 +9240,27 @@ } } }, - "/api/v1/admin/events/{id}/organizers/": { - "put": { - "operationId": "api_v1_admin_events_organizers_update", - "summary": "Replace an event's organization organizers", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/admin/event-staff-invites/redeem/": { + "post": { + "operationId": "api_v1_admin_event_staff_invites_redeem_create", "tags": [ - "Admin events" + "Events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventOrganizerReplace" + "$ref": "#/components/schemas/StaffGrantRedeem" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/EventOrganizerReplace" + "$ref": "#/components/schemas/StaffGrantRedeem" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/EventOrganizerReplace" + "$ref": "#/components/schemas/StaffGrantRedeem" } } }, @@ -8827,14 +9276,21 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventOrganizerList" + "$ref": "#/components/schemas/StaffGrant" } } }, "description": "" }, "400": { - "description": "Invalid or unavailable organization." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "401": { "content": { @@ -8867,62 +9323,26 @@ "description": "" }, "409": { - "description": "Concurrent event state conflict." - } - } - } - }, - "/api/v1/admin/events/{id}/promo-codes/": { - "get": { - "operationId": "api_v1_admin_events_promo_codes_retrieve", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - } - ], - "tags": [ - "Admin events" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PromoList" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "423": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid field values, disabled events module or disabled feature." + "description": "" }, - "401": { + "429": { "content": { "application/json": { "schema": { @@ -8932,7 +9352,7 @@ }, "description": "" }, - "403": { + "503": { "content": { "application/json": { "schema": { @@ -8941,18 +9361,44 @@ } }, "description": "" - }, - "404": { + } + } + } + }, + "/api/v1/admin/events/{id}/": { + "get": { + "operationId": "api_v1_admin_events_retrieve", + "summary": "Retrieve an event", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/EventAdmin" } } }, "description": "" }, - "409": { + "400": { "content": { "application/json": { "schema": { @@ -8962,7 +9408,7 @@ }, "description": "" }, - "423": { + "401": { "content": { "application/json": { "schema": { @@ -8972,7 +9418,7 @@ }, "description": "" }, - "429": { + "403": { "content": { "application/json": { "schema": { @@ -8982,7 +9428,7 @@ }, "description": "" }, - "503": { + "404": { "content": { "application/json": { "schema": { @@ -8994,8 +9440,9 @@ } } }, - "post": { - "operationId": "api_v1_admin_events_promo_codes_create", + "patch": { + "operationId": "api_v1_admin_events_partial_update", + "summary": "Update an event", "parameters": [ { "in": "path", @@ -9013,17 +9460,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PromoInput" + "$ref": "#/components/schemas/PatchedEventWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PromoInput" + "$ref": "#/components/schemas/PatchedEventWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PromoInput" + "$ref": "#/components/schemas/PatchedEventWrite" } } } @@ -9034,11 +9481,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Promo" + "$ref": "#/components/schemas/EventAdmin" } } }, @@ -9053,37 +9500,7 @@ } } }, - "description": "Invalid field values, disabled events module or disabled feature." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Invalid event details." }, "409": { "content": { @@ -9093,9 +9510,9 @@ } } }, - "description": "" + "description": "Event state or capacity conflict." }, - "423": { + "401": { "content": { "application/json": { "schema": { @@ -9105,7 +9522,7 @@ }, "description": "" }, - "429": { + "403": { "content": { "application/json": { "schema": { @@ -9115,7 +9532,7 @@ }, "description": "" }, - "503": { + "404": { "content": { "application/json": { "schema": { @@ -9128,10 +9545,9 @@ } } }, - "/api/v1/admin/events/{id}/publish/": { - "post": { - "operationId": "api_v1_admin_events_publish_create", - "summary": "Publish an event", + "/api/v1/admin/events/{id}/badge-template/": { + "get": { + "operationId": "api_v1_admin_events_badge_template_retrieve", "parameters": [ { "in": "path", @@ -9155,28 +9571,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventAdmin" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Invalid event details." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/BadgeTemplate" } } }, @@ -9201,24 +9596,11 @@ } }, "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Event state or capacity conflict." } } - } - }, - "/api/v1/admin/events/{id}/registrations/": { - "get": { - "operationId": "api_v1_admin_events_registrations_retrieve", - "summary": "List event registrations", + }, + "put": { + "operationId": "api_v1_admin_events_badge_template_update", "parameters": [ { "in": "path", @@ -9232,6 +9614,25 @@ "tags": [ "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadgeTemplate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BadgeTemplate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BadgeTemplate" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -9242,7 +9643,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventRegistrationListResponse" + "$ref": "#/components/schemas/BadgeTemplate" } } }, @@ -9257,17 +9658,7 @@ } } }, - "description": "Unexpected request body or events module disabled." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication is required." + "description": "Invalid event details." }, "403": { "content": { @@ -9277,7 +9668,7 @@ } } }, - "description": "Event management is required." + "description": "" }, "404": { "content": { @@ -9287,7 +9678,7 @@ } } }, - "description": "Registration not found." + "description": "" }, "409": { "content": { @@ -9300,10 +9691,12 @@ "description": "Event state or capacity conflict." } } - }, + } + }, + "/api/v1/admin/events/{id}/badges.pdf": { "post": { - "operationId": "api_v1_admin_events_registrations_create", - "summary": "Register a member for an event", + "operationId": "api_v1_admin_events_badges.pdf_create", + "description": "Private audited positioned badge PDF, 30/hour per account. No contacts or bearer QR. Custom answers require registration management and tenant VIEW_AUDIT.", "parameters": [ { "in": "path", @@ -9321,17 +9714,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventStaffRegistration" + "$ref": "#/components/schemas/BadgePdfRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/EventStaffRegistration" + "$ref": "#/components/schemas/BadgePdfRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/EventStaffRegistration" + "$ref": "#/components/schemas/BadgePdfRequest" } } }, @@ -9343,27 +9736,29 @@ } ], "responses": { - "201": { + "200": { "content": { - "application/json": { + "application/pdf": { "schema": { - "$ref": "#/components/schemas/EventRegistrationAdmin" + "type": "string", + "format": "binary" } } }, - "description": "" + "description": "Print-ready badge PDF." }, "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid registration." + "description": "Invalid event details." }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -9373,7 +9768,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -9383,7 +9778,7 @@ }, "description": "" }, - "404": { + "409": { "content": { "application/json": { "schema": { @@ -9391,9 +9786,9 @@ } } }, - "description": "" + "description": "Event state or capacity conflict." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -9401,14 +9796,15 @@ } } }, - "description": "Event state or capacity conflict." + "description": "" } } } }, - "/api/v1/admin/events/{id}/ticket-types/": { + "/api/v1/admin/events/{id}/broadcasts/": { "get": { - "operationId": "api_v1_admin_events_ticket_types_retrieve", + "operationId": "api_v1_admin_events_broadcasts_retrieve", + "description": "Requires live event manage_registrations authority and events.broadcasts, notifications and email.", "parameters": [ { "in": "path", @@ -9439,7 +9835,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TicketList" + "$ref": "#/components/schemas/EventBroadcastList" } } }, @@ -9529,7 +9925,8 @@ } }, "post": { - "operationId": "api_v1_admin_events_ticket_types_create", + "operationId": "api_v1_admin_events_broadcasts_create", + "description": "Compose an audited draft. Requires manage_registrations and events.broadcasts, notifications and email; placeholders never include credentials.", "parameters": [ { "in": "path", @@ -9547,17 +9944,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TicketInput" + "$ref": "#/components/schemas/EventBroadcastInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/TicketInput" + "$ref": "#/components/schemas/EventBroadcastInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/TicketInput" + "$ref": "#/components/schemas/EventBroadcastInput" } } }, @@ -9573,7 +9970,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Ticket" + "$ref": "#/components/schemas/EventBroadcast" } } }, @@ -9663,46 +10060,19 @@ } } }, - "/api/v1/admin/evidence/{id}": { - "get": { - "operationId": "api_v1_admin_evidence_retrieve", + "/api/v1/admin/events/{id}/broadcasts/{campaign_id}/cancel/": { + "post": { + "operationId": "api_v1_admin_events_broadcasts_cancel_create", + "description": "Queue or cancel under live event authority. Queue limit: ten broadcasts per hour per makerspace. Cancellation stops unclaimed sends.", "parameters": [ { "in": "path", - "name": "id", + "name": "campaign_id", "schema": { "type": "integer" }, "required": true - } - ], - "tags": [ - "api" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EvidenceGetResponse" - } - } - }, - "description": "" - } - } - } - }, - "/api/v1/admin/inventory/{id}": { - "get": { - "operationId": "api_v1_admin_inventory_retrieve", - "summary": "Retrieve or update inventory product", - "parameters": [ + }, { "in": "path", "name": "id", @@ -9713,7 +10083,7 @@ } ], "tags": [ - "Admin inventory" + "Admin events" ], "security": [ { @@ -9725,30 +10095,109 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InventoryProductAdmin" + "$ref": "#/components/schemas/EventBroadcast" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid field values, disabled events module or disabled feature." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } - }, - "patch": { - "operationId": "api_v1_admin_inventory_partial_update", - "summary": "Retrieve or update inventory product", + } + }, + "/api/v1/admin/events/{id}/broadcasts/{campaign_id}/queue/": { + "post": { + "operationId": "api_v1_admin_events_broadcasts_queue_create", + "description": "Queue or cancel under live event authority. Queue limit: ten broadcasts per hour per makerspace. Cancellation stops unclaimed sends.", "parameters": [ + { + "in": "path", + "name": "campaign_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "id", @@ -9759,27 +10208,8 @@ } ], "tags": [ - "Admin inventory" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedInventoryProductAdminUpdate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedInventoryProductAdminUpdate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedInventoryProductAdminUpdate" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -9790,78 +10220,88 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InventoryProductAdminUpdate" + "$ref": "#/components/schemas/EventBroadcast" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid field values, disabled events module or disabled feature." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." - } - } - } - }, - "/api/v1/admin/inventory/{id}/adjust-quantity": { - "post": { - "operationId": "api_v1_admin_inventory_adjust_quantity_create", - "summary": "Adjust inventory quantity buckets", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin inventory" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InventoryQuantityAdjustment" + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/InventoryQuantityAdjustment" + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/InventoryQuantityAdjustment" + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InventoryProductAdmin" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -9870,10 +10310,10 @@ } } }, - "/api/v1/admin/inventory/{id}/chain-of-custody": { - "get": { - "operationId": "api_v1_admin_inventory_chain_of_custody_retrieve", - "summary": "Read-only chain of custody for one inventory item", + "/api/v1/admin/events/{id}/cancel/": { + "post": { + "operationId": "api_v1_admin_events_cancel_create", + "summary": "Cancel an event", "parameters": [ { "in": "path", @@ -9882,18 +10322,10 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - }, - "description": "Maximum history events to return. Defaults to 200; capped at 500." } ], "tags": [ - "Admin inventory" + "Admin events" ], "security": [ { @@ -9905,7 +10337,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InventoryChainOfCustodyResponse" + "$ref": "#/components/schemas/EventAdmin" } } }, @@ -9919,7 +10351,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -9929,7 +10361,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -9939,7 +10371,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -9949,7 +10381,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -9959,15 +10391,15 @@ } } }, - "description": "Workflow conflict." + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/inventory/{id}/image": { + "/api/v1/admin/events/{id}/certificate-artwork/": { "post": { - "operationId": "api_v1_admin_inventory_image_create", - "summary": "Create an inventory product image upload URL", + "operationId": "api_v1_admin_events_certificate_artwork_create", + "description": "Reserve owned private artwork and issue a ten-minute staging-only PUT. PNG/JPEG, at most 10 MiB. Final bytes are server-owned.", "parameters": [ { "in": "path", @@ -9979,23 +10411,23 @@ } ], "tags": [ - "Admin inventory" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "$ref": "#/components/schemas/CertificateArtworkPresign" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "$ref": "#/components/schemas/CertificateArtworkPresign" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "$ref": "#/components/schemas/CertificateArtworkPresign" } } }, @@ -10011,84 +10443,98 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadResponse" + "$ref": "#/components/schemas/CertificateArtworkUpload" } } }, "description": "" }, "400": { - "description": "Invalid image upload request." - }, - "503": { - "description": "Public image storage is unavailable." - } - } - }, - "put": { - "operationId": "api_v1_admin_inventory_image_update", - "summary": "Attach an uploaded image to an inventory product", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin inventory" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InventoryProductAdmin" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Invalid image object key or size." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "503": { - "description": "Public image storage is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } - }, - "delete": { - "operationId": "api_v1_admin_inventory_image_destroy", - "summary": "Clear an inventory product image", + } + }, + "/api/v1/admin/events/{id}/certificate-artwork/{artwork_id}/": { + "get": { + "operationId": "api_v1_admin_events_certificate_artwork_retrieve", + "description": "Read private artwork with live event edit authority; no public artwork route or object URL.", "parameters": [ + { + "in": "path", + "name": "artwork_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "id", @@ -10099,7 +10545,7 @@ } ], "tags": [ - "Admin inventory" + "Admin events" ], "security": [ { @@ -10108,10 +10554,81 @@ ], "responses": { "200": { + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InventoryProductAdmin" + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -10120,11 +10637,19 @@ } } }, - "/api/v1/admin/inventory/{id}/lending-history": { - "get": { - "operationId": "api_v1_admin_inventory_lending_history_retrieve", - "summary": "Per-item lending history (last borrower + last 3 lends)", + "/api/v1/admin/events/{id}/certificate-artwork/{artwork_id}/finalize/": { + "post": { + "operationId": "api_v1_admin_events_certificate_artwork_finalize_create", + "description": "Validate bounded decoded pixels, strip metadata and promote exactly once. Quota and authority rechecked on attachment. Retries are idempotent.", "parameters": [ + { + "in": "path", + "name": "artwork_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "id", @@ -10135,7 +10660,7 @@ } ], "tags": [ - "Admin inventory" + "Admin events" ], "security": [ { @@ -10147,64 +10672,77 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LendingHistoryResponse" + "$ref": "#/components/schemas/CertificateArtwork" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/inventory/{id}/needs-fix": { - "post": { - "operationId": "api_v1_admin_inventory_needs_fix_create", - "description": "Move units onto the shelf, back to available, or out of inventory.", - "summary": "Move inventory units to, from, or out of the to-be-fixed shelf", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin inventory" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NeedsFixAction" + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/NeedsFixAction" + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/NeedsFixAction" + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InventoryProductAdmin" + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -10213,10 +10751,10 @@ } } }, - "/api/v1/admin/inventory/{id}/qr-history": { + "/api/v1/admin/events/{id}/certificate-template/": { "get": { - "operationId": "api_v1_admin_inventory_qr_history_retrieve", - "summary": "List QR scan history for an inventory product", + "operationId": "api_v1_admin_events_certificate_template_retrieve", + "description": "Current immutable revision or default layout. Requires live edit_details and events.certificate_designer.", "parameters": [ { "in": "path", @@ -10228,7 +10766,7 @@ } ], "tags": [ - "Admin inventory" + "Admin events" ], "security": [ { @@ -10240,120 +10778,87 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProductQrHistory" + "$ref": "#/components/schemas/CertificateTemplateRevision" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/inventory/{product_pk}/assets": { - "get": { - "operationId": "api_v1_admin_inventory_assets_list", - "summary": "List individual assets for an inventory product", - "parameters": [ - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } }, - { - "name": "page_size", - "required": false, - "in": "query", - "description": "Number of results to return per page.", - "schema": { - "type": "integer" - } + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - { - "in": "path", - "name": "product_pk", - "schema": { - "type": "integer" + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin inventory" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedInventoryAssetAdminList" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/inventory/needs-fix": { - "get": { - "operationId": "api_v1_admin_inventory_needs_fix_list", - "description": "The to-be-fixed shelf: products that currently have units awaiting repair.", - "parameters": [ - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } }, - { - "name": "page_size", - "required": false, - "in": "query", - "description": "Number of results to return per page.", - "schema": { - "type": "integer" - } - } - ], - "tags": [ - "api" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedInventoryProductAdminList" + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" } } - } - }, - "/api/v1/admin/invitation-requests/{id}/decline": { + }, "post": { - "operationId": "api_v1_admin_invitation_requests_decline_create", - "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", - "summary": "Decline an invitation request", + "operationId": "api_v1_admin_events_certificate_template_create", + "description": "Create a new immutable revision. Artwork must be promoted and owned by this event; issuance snapshots never change.", "parameters": [ { "in": "path", @@ -10365,19 +10870,39 @@ } ], "tags": [ - "Admin memberships" + "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CertificateTemplateWrite" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CertificateTemplateWrite" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CertificateTemplateWrite" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InvitationRequest" + "$ref": "#/components/schemas/CertificateTemplateRevision" } } }, @@ -10432,15 +10957,34 @@ } }, "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/invitation-requests/{id}/invite": { + "/api/v1/admin/events/{id}/certificate-template/preview.pdf": { "post": { - "operationId": "api_v1_admin_invitation_requests_invite_create", - "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", - "summary": "Turn an invitation request into a membership invitation", + "operationId": "api_v1_admin_events_certificate_template_preview.pdf_create", + "description": "Preview with fixed sample recipient; same geometry, artwork and overflow rules as issuance. No recipient information is accepted.", "parameters": [ { "in": "path", @@ -10452,23 +10996,23 @@ } ], "tags": [ - "Admin memberships" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RoleId" + "$ref": "#/components/schemas/CertificateTemplateWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/RoleId" + "$ref": "#/components/schemas/CertificateTemplateWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/RoleId" + "$ref": "#/components/schemas/CertificateTemplateWrite" } } }, @@ -10482,13 +11026,14 @@ "responses": { "200": { "content": { - "application/json": { + "application/pdf": { "schema": { - "$ref": "#/components/schemas/InvitationRequest" + "type": "string", + "format": "binary" } } }, - "description": "" + "description": "Private server-rendered certificate preview." }, "400": { "content": { @@ -10539,27 +11084,46 @@ } }, "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/kiosk-replays/{id}/": { + "/api/v1/admin/events/{id}/check-in/offline-roster/": { "get": { - "operationId": "api_v1_admin_kiosk_replays_retrieve", - "summary": "Inspect one current-action-scoped failed handover", + "operationId": "api_v1_admin_events_check_in_offline_roster_retrieve", + "summary": "Download a minimal expiring offline check-in roster", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Admin kiosk recovery" + "Admin events" ], "security": [ { @@ -10571,7 +11135,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReplayRecovery" + "$ref": "#/components/schemas/OfflineRosterResponse" } } }, @@ -10585,7 +11149,7 @@ } } }, - "description": "Invalid filter/resolution or disabled required module." + "description": "Feature disabled or invalid batch." }, "401": { "content": { @@ -10595,7 +11159,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication or lease failed." }, "403": { "content": { @@ -10605,7 +11169,7 @@ } } }, - "description": "Account, current action or origin refused." + "description": "Event authority changed." }, "404": { "content": { @@ -10615,7 +11179,7 @@ } } }, - "description": "Failure is unavailable in your scope." + "description": "Event not found." }, "409": { "content": { @@ -10625,9 +11189,9 @@ } } }, - "description": "Resolution/replacement conflict." + "description": "Roster window closed." }, - "423": { + "410": { "content": { "application/json": { "schema": { @@ -10635,45 +11199,63 @@ } } }, - "description": "Tenant source gate closed." + "description": "Synchronization deadline passed." + }, + "413": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Roster exceeds the offline limit." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." } } } }, - "/api/v1/admin/kiosk-replays/{id}/resolve/": { + "/api/v1/admin/events/{id}/check-in/offline-sync/": { "post": { - "operationId": "api_v1_admin_kiosk_replays_resolve_create", - "description": "Acknowledgement records no physical change. Replacement links only a fresh succeeded replay for the same known subject; this endpoint never executes handovers.", - "summary": "Append an idempotent review of a failed handover", + "operationId": "api_v1_admin_events_check_in_offline_sync_create", + "summary": "Synchronize queued offline event check-ins", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Admin kiosk recovery" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReplayResolution" + "$ref": "#/components/schemas/OfflineCheckInSyncRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ReplayResolution" + "$ref": "#/components/schemas/OfflineCheckInSyncRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ReplayResolution" + "$ref": "#/components/schemas/OfflineCheckInSyncRequest" } } }, @@ -10689,7 +11271,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReplayRecovery" + "$ref": "#/components/schemas/OfflineCheckInSyncResponse" } } }, @@ -10703,7 +11285,7 @@ } } }, - "description": "Invalid filter/resolution or disabled required module." + "description": "Feature disabled or invalid batch." }, "401": { "content": { @@ -10713,7 +11295,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication or lease failed." }, "403": { "content": { @@ -10723,7 +11305,7 @@ } } }, - "description": "Account, current action or origin refused." + "description": "Event authority changed." }, "404": { "content": { @@ -10733,7 +11315,7 @@ } } }, - "description": "Failure is unavailable in your scope." + "description": "Event not found." }, "409": { "content": { @@ -10743,9 +11325,9 @@ } } }, - "description": "Resolution/replacement conflict." + "description": "Roster window closed." }, - "423": { + "410": { "content": { "application/json": { "schema": { @@ -10753,15 +11335,35 @@ } } }, - "description": "Tenant source gate closed." + "description": "Synchronization deadline passed." + }, + "413": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Roster exceeds the offline limit." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." } } } }, - "/api/v1/admin/kiosk-stations/{id}/": { - "get": { - "operationId": "api_v1_admin_kiosk_stations_retrieve", - "summary": "Read one kiosk Station without its PIN", + "/api/v1/admin/events/{id}/check-in/resolve/": { + "post": { + "operationId": "api_v1_admin_events_check_in_resolve_create", + "summary": "Resolve an event check-in token", "parameters": [ { "in": "path", @@ -10773,8 +11375,28 @@ } ], "tags": [ - "Admin kiosk stations" + "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventCheckInResolveRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/EventCheckInResolveRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/EventCheckInResolveRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -10785,22 +11407,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskStation" + "$ref": "#/components/schemas/EventCheckInResolveResponse" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Feature disabled or invalid request." - }, "403": { "content": { "application/json": { @@ -10809,7 +11421,7 @@ } } }, - "description": "Makerspace access or step-up denied." + "description": "Event access denied." }, "404": { "content": { @@ -10819,17 +11431,7 @@ } } }, - "description": "Station not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rotate the PIN instead of revealing." + "description": "Registration not found." }, "429": { "content": { @@ -10840,22 +11442,14 @@ } }, "description": "Request rate limit exceeded." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Credential secrets are unavailable." } } - }, - "delete": { - "operationId": "api_v1_admin_kiosk_stations_destroy", - "summary": "Disable a kiosk Station and revoke its leases", + } + }, + "/api/v1/admin/events/{id}/check-in/station/": { + "get": { + "operationId": "api_v1_admin_events_check_in_station_retrieve", + "summary": "Read venue-station configuration without revealing its PIN", "parameters": [ { "in": "path", @@ -10867,7 +11461,7 @@ } ], "tags": [ - "Admin kiosk stations" + "Admin events" ], "security": [ { @@ -10879,7 +11473,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskStation" + "$ref": "#/components/schemas/StationStatus" } } }, @@ -10903,7 +11497,7 @@ } } }, - "description": "Makerspace access or step-up denied." + "description": "Event access or step-up denied." }, "404": { "content": { @@ -10913,7 +11507,7 @@ } } }, - "description": "Station not found." + "description": "Event not found." }, "409": { "content": { @@ -10946,12 +11540,10 @@ "description": "Credential secrets are unavailable." } } - } - }, - "/api/v1/admin/kiosk-stations/{id}/reveal/": { - "post": { - "operationId": "api_v1_admin_kiosk_stations_reveal_create", - "summary": "Reveal a kiosk Station PIN after password step-up", + }, + "delete": { + "operationId": "api_v1_admin_events_check_in_station_destroy", + "summary": "Disable a venue check-in station", "parameters": [ { "in": "path", @@ -10963,28 +11555,8 @@ } ], "tags": [ - "Admin kiosk stations" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/KioskStationReveal" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/KioskStationReveal" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/KioskStationReveal" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -10995,7 +11567,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskStationRevealResponse" + "$ref": "#/components/schemas/StationStatus" } } }, @@ -11019,7 +11591,7 @@ } } }, - "description": "Makerspace access or step-up denied." + "description": "Event access or step-up denied." }, "404": { "content": { @@ -11029,7 +11601,7 @@ } } }, - "description": "Station not found." + "description": "Event not found." }, "409": { "content": { @@ -11064,10 +11636,10 @@ } } }, - "/api/v1/admin/kiosk-stations/{id}/rotate/": { + "/api/v1/admin/events/{id}/check-in/station/reveal/": { "post": { - "operationId": "api_v1_admin_kiosk_stations_rotate_create", - "summary": "Rotate a kiosk Station PIN (re-enables it)", + "operationId": "api_v1_admin_events_check_in_station_reveal_create", + "summary": "Reveal the current station PIN after password step-up", "parameters": [ { "in": "path", @@ -11079,8 +11651,28 @@ } ], "tags": [ - "Admin kiosk stations" + "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StationReveal" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StationReveal" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StationReveal" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -11091,7 +11683,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskStationSecret" + "$ref": "#/components/schemas/StationRevealResponse" } } }, @@ -11115,7 +11707,7 @@ } } }, - "description": "Makerspace access or step-up denied." + "description": "Event access or step-up denied." }, "404": { "content": { @@ -11125,7 +11717,7 @@ } } }, - "description": "Station not found." + "description": "Event not found." }, "409": { "content": { @@ -11160,84 +11752,22 @@ } } }, - "/api/v1/admin/ledger": { - "get": { - "operationId": "api_v1_admin_ledger_retrieve", - "summary": "List outstanding inventory loans across all makerspaces", + "/api/v1/admin/events/{id}/check-in/station/rotate/": { + "post": { + "operationId": "api_v1_admin_events_check_in_station_rotate_create", + "summary": "Create or rotate an event-scoped eight-digit station PIN", "parameters": [ { - "in": "query", - "name": "makerspace", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "overdue", - "schema": { - "type": "boolean" - } - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "page_size", + "in": "path", + "name": "id", "schema": { "type": "integer" - } - }, - { - "in": "query", - "name": "search", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string", - "enum": [ - "-due", - "-holder", - "-item_name", - "-makerspace_id", - "-quantity", - "-since", - "-source", - "due", - "holder", - "item_name", - "makerspace_id", - "quantity", - "since", - "source" - ] - } - }, - { - "in": "query", - "name": "source", - "schema": { - "type": "string", - "enum": [ - "direct", - "reviewed", - "self_checkout" - ] - } + }, + "required": true } ], "tags": [ - "Ledger" + "Admin events" ], "security": [ { @@ -11249,165 +11779,43 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LedgerResponse" + "$ref": "#/components/schemas/StationRotation" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/ledger/export": { - "get": { - "operationId": "api_v1_admin_ledger_export_retrieve", - "summary": "Export outstanding inventory loans across all makerspaces", - "parameters": [ - { - "in": "query", - "name": "format", - "schema": { - "type": "string", - "enum": [ - "csv", - "xlsx" - ] - } - }, - { - "in": "query", - "name": "makerspace", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "overdue", - "schema": { - "type": "boolean" - } - }, - { - "in": "query", - "name": "search", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string", - "enum": [ - "-due", - "-holder", - "-item_name", - "-makerspace_id", - "-quantity", - "-since", - "-source", - "due", - "holder", - "item_name", - "makerspace_id", - "quantity", - "since", - "source" - ] - } }, - { - "in": "query", - "name": "source", - "schema": { - "type": "string", - "enum": [ - "direct", - "reviewed", - "self_checkout" - ] - } - } - ], - "tags": [ - "Ledger" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "400": { "content": { - "text/csv": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/HardwareRequestError" } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + } + }, + "description": "Feature disabled or invalid request." + }, + "403": { + "content": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - } - } - } - }, - "/api/v1/admin/machine-service-report": { - "get": { - "operationId": "api_v1_admin_machine_service_report_retrieve", - "summary": "Retrieve aggregate machine-service report", - "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "machine_type", - "schema": { - "type": "string" - } + "description": "Event access or step-up denied." }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - } - ], - "tags": [ - "Admin machine service" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceReportResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Event not found." }, - "400": { + "409": { "content": { "application/json": { "schema": { @@ -11415,23 +11823,35 @@ } } }, - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." + "description": "Rotate the PIN instead of revealing." }, - "403": { - "description": "Machine management permission required." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." }, - "404": { - "description": "Not found." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Credential secrets are unavailable." } } } }, - "/api/v1/admin/machine-service/consumable-pools/{id}": { + "/api/v1/admin/events/{id}/collaborators/": { "get": { - "operationId": "api_v1_admin_machine_service_consumable_pools_retrieve", + "operationId": "api_v1_admin_events_collaborators_list", + "summary": "List an event's collaborators", "parameters": [ { "in": "path", @@ -11443,7 +11863,7 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], "security": [ { @@ -11455,70 +11875,50 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrinterPool" + "type": "array", + "items": { + "$ref": "#/components/schemas/EventCollaborator" + } } } }, "description": "" - } - } - }, - "patch": { - "operationId": "api_v1_admin_machine_service_consumable_pools_partial_update", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machine service" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedPrinterPoolVisibility" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedPrinterPoolVisibility" + "description": "Invalid collaboration request." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedPrinterPoolVisibility" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "Event management access denied." + }, + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrinterPool" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Event collaboration not found." } } - } - }, - "/api/v1/admin/machine-service/consumable-pools/{id}/adjustments": { - "post": { - "operationId": "api_v1_admin_machine_service_consumable_pools_adjustments_create", + }, + "put": { + "operationId": "api_v1_admin_events_collaborators_update", + "summary": "Replace an event's collaborators", "parameters": [ { "in": "path", @@ -11530,23 +11930,23 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrinterPoolCorrection" + "$ref": "#/components/schemas/EventCollaboratorReplace" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PrinterPoolCorrection" + "$ref": "#/components/schemas/EventCollaboratorReplace" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PrinterPoolCorrection" + "$ref": "#/components/schemas/EventCollaboratorReplace" } } }, @@ -11562,40 +11962,14 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrinterPool" + "type": "array", + "items": { + "$ref": "#/components/schemas/EventCollaborator" + } } } }, "description": "" - } - } - } - }, - "/api/v1/admin/machine-service/files/{id}": { - "delete": { - "operationId": "api_v1_admin_machine_service_files_destroy", - "summary": "Delete a staged service attachment", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machine service" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" }, "400": { "content": { @@ -11605,18 +11979,19 @@ } } }, - "description": "Invalid attachment input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid collaboration request." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management access denied." }, "404": { - "description": "Service request or file was not found." - }, - "409": { "content": { "application/json": { "schema": { @@ -11624,18 +11999,15 @@ } } }, - "description": "Attachment conflict." - }, - "503": { - "description": "Private storage is unavailable." + "description": "Event collaboration not found." } } } }, - "/api/v1/admin/machine-service/files/{id}/url": { - "get": { - "operationId": "api_v1_admin_machine_service_files_url_retrieve", - "summary": "Create a signed service attachment URL", + "/api/v1/admin/events/{id}/complete/": { + "post": { + "operationId": "api_v1_admin_events_complete_create", + "summary": "Complete an event", "parameters": [ { "in": "path", @@ -11647,7 +12019,7 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], "security": [ { @@ -11659,7 +12031,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServiceFileUrl" + "$ref": "#/components/schemas/EventAdmin" } } }, @@ -11673,16 +12045,37 @@ } } }, - "description": "Invalid attachment input." + "description": "" }, "401": { - "description": "Authentication required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Service request or file was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "409": { "content": { @@ -11692,18 +12085,15 @@ } } }, - "description": "Attachment conflict." - }, - "503": { - "description": "Private storage is unavailable." + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/machine-service/payments/{id}/mark-offline": { + "/api/v1/admin/events/{id}/duplicate/": { "post": { - "operationId": "api_v1_admin_machine_service_payments_mark_offline_create", - "summary": "Mark a machine-service payment paid offline", + "operationId": "api_v1_admin_events_duplicate_create", + "summary": "Duplicate event into a standalone draft", "parameters": [ { "in": "path", @@ -11715,27 +12105,26 @@ } ], "tags": [ - "Payments" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentOffline" + "$ref": "#/components/schemas/EventDuplicate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PaymentOffline" + "$ref": "#/components/schemas/EventDuplicate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PaymentOffline" + "$ref": "#/components/schemas/EventDuplicate" } } - }, - "required": true + } }, "security": [ { @@ -11743,11 +12132,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffPayment" + "$ref": "#/components/schemas/EventAdmin" } } }, @@ -11761,7 +12150,7 @@ } } }, - "description": "Missing or invalid settlement receipt." + "description": "" }, "401": { "content": { @@ -11771,7 +12160,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -11781,7 +12170,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -11791,7 +12180,7 @@ } } }, - "description": "Payment not found or out of machine scope." + "description": "" }, "409": { "content": { @@ -11801,15 +12190,26 @@ } } }, - "description": "`payment_terminal`: already reconciled." + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/machine-service/payments/{id}/waive": { - "post": { - "operationId": "api_v1_admin_machine_service_payments_waive_create", - "summary": "Waive a machine-service payment", + "/api/v1/admin/events/{id}/eligible-members/": { + "get": { + "operationId": "api_v1_admin_events_eligible_members_list", + "description": "The roster the staff registration picker reads.\n\nHung off the EVENT rather than the makerspace, so it inherits `_manageable_event`\nand introduces no new authority question: whoever may manage this event may see who\nthey can register for it. A separate makerspace-level member list would have needed\nits own permission answer, and the obvious candidates were all wrong — the\ndirect-loan roster is gated on `ISSUE_DIRECT_LOAN` plus a self-checkout feature an\nevents manager need not hold, and the full membership list is `MANAGE_MAKERSPACE`.\n\nAlready-registered members are excluded: offering someone the picker can only reject\nas a duplicate is an error the interface should not have made available.", + "summary": "List members who can be registered for an event", "parameters": [ { "in": "path", @@ -11821,7 +12221,7 @@ } ], "tags": [ - "Payments" + "Admin events" ], "security": [ { @@ -11833,12 +12233,26 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffPayment" + "type": "array", + "items": { + "$ref": "#/components/schemas/EventEligibleMember" + } } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Unexpected request body or events module disabled." + }, "401": { "content": { "application/json": { @@ -11847,7 +12261,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -11857,7 +12271,7 @@ } } }, - "description": "Permission denied." + "description": "Event management is required." }, "404": { "content": { @@ -11867,7 +12281,7 @@ } } }, - "description": "Payment not found or out of machine scope." + "description": "Registration not found." }, "409": { "content": { @@ -11877,15 +12291,14 @@ } } }, - "description": "`payment_terminal`: already reconciled." + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/machine-service/requests/{id}": { + "/api/v1/admin/events/{id}/feedback-responses/": { "get": { - "operationId": "api_v1_admin_machine_service_requests_retrieve", - "summary": "Retrieve a machine service request", + "operationId": "api_v1_admin_events_feedback_responses_retrieve", "parameters": [ { "in": "path", @@ -11897,7 +12310,7 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], "security": [ { @@ -11909,7 +12322,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "$ref": "#/components/schemas/FeedbackResponseList" } } }, @@ -11923,16 +12336,27 @@ } } }, - "description": "Invalid service request input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid request." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management is required." }, "404": { - "description": "Service request was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event resource not found." }, "409": { "content": { @@ -11942,15 +12366,14 @@ } } }, - "description": "Service workflow conflict." + "description": "Event state conflict." } } } }, - "/api/v1/admin/machine-service/requests/{id}/accept": { - "post": { - "operationId": "api_v1_admin_machine_service_requests_accept_create", - "summary": "Accept a machine service request", + "/api/v1/admin/events/{id}/feedback-survey/": { + "get": { + "operationId": "api_v1_admin_events_feedback_survey_retrieve", "parameters": [ { "in": "path", @@ -11962,27 +12385,8 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServiceAccept" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ServiceAccept" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ServiceAccept" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -11993,7 +12397,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "$ref": "#/components/schemas/FeedbackSurveyAdminEnvelope" } } }, @@ -12007,16 +12411,27 @@ } } }, - "description": "Invalid service request input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid request." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management is required." }, "404": { - "description": "Service request was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event resource not found." }, "409": { "content": { @@ -12026,15 +12441,12 @@ } } }, - "description": "Service workflow conflict." + "description": "Event state conflict." } } - } - }, - "/api/v1/admin/machine-service/requests/{id}/collect": { - "post": { - "operationId": "api_v1_admin_machine_service_requests_collect_create", - "summary": "Mark a machine service request collected", + }, + "put": { + "operationId": "api_v1_admin_events_feedback_survey_update", "parameters": [ { "in": "path", @@ -12046,8 +12458,28 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackSurveyWrite" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/FeedbackSurveyWrite" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/FeedbackSurveyWrite" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -12058,7 +12490,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "$ref": "#/components/schemas/FeedbackSurvey" } } }, @@ -12072,16 +12504,27 @@ } } }, - "description": "Invalid service request input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid request." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management is required." }, "404": { - "description": "Service request was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event resource not found." }, "409": { "content": { @@ -12091,15 +12534,14 @@ } } }, - "description": "Service workflow conflict." + "description": "Event state conflict." } } } }, - "/api/v1/admin/machine-service/requests/{id}/complete": { + "/api/v1/admin/events/{id}/feedback-survey/close/": { "post": { - "operationId": "api_v1_admin_machine_service_requests_complete_create", - "summary": "Complete machine service work", + "operationId": "api_v1_admin_events_feedback_survey_close_create", "parameters": [ { "in": "path", @@ -12111,28 +12553,8 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServiceComplete" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ServiceComplete" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ServiceComplete" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -12143,7 +12565,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "$ref": "#/components/schemas/FeedbackSurvey" } } }, @@ -12157,16 +12579,27 @@ } } }, - "description": "Invalid service request input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid request." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management is required." }, "404": { - "description": "Service request was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event resource not found." }, "409": { "content": { @@ -12176,15 +12609,14 @@ } } }, - "description": "Service workflow conflict." + "description": "Event state conflict." } } } }, - "/api/v1/admin/machine-service/requests/{id}/fail": { + "/api/v1/admin/events/{id}/feedback-survey/open/": { "post": { - "operationId": "api_v1_admin_machine_service_requests_fail_create", - "summary": "Mark machine service work failed", + "operationId": "api_v1_admin_events_feedback_survey_open_create", "parameters": [ { "in": "path", @@ -12196,28 +12628,8 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServiceFail" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ServiceFail" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ServiceFail" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -12228,7 +12640,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "$ref": "#/components/schemas/FeedbackSurvey" } } }, @@ -12242,16 +12654,27 @@ } } }, - "description": "Invalid service request input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid request." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management is required." }, "404": { - "description": "Service request was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event resource not found." }, "409": { "content": { @@ -12261,15 +12684,15 @@ } } }, - "description": "Service workflow conflict." + "description": "Event state conflict." } } } }, - "/api/v1/admin/machine-service/requests/{id}/files/finalize": { + "/api/v1/admin/events/{id}/feedback-survey/templates/": { "post": { - "operationId": "api_v1_admin_machine_service_requests_files_finalize_create", - "summary": "Finalize a service attachment upload", + "operationId": "api_v1_admin_events_feedback_survey_templates_create", + "summary": "Apply a feedback template before any response", "parameters": [ { "in": "path", @@ -12281,23 +12704,23 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServiceFileFinalize" + "$ref": "#/components/schemas/FeedbackTemplate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ServiceFileFinalize" + "$ref": "#/components/schemas/FeedbackTemplate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ServiceFileFinalize" + "$ref": "#/components/schemas/FeedbackTemplate" } } }, @@ -12309,11 +12732,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServiceFileFinalizeResponse" + "$ref": "#/components/schemas/FeedbackSurvey" } } }, @@ -12327,16 +12750,37 @@ } } }, - "description": "Invalid attachment input." + "description": "" }, "401": { - "description": "Authentication required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Service request or file was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "409": { "content": { @@ -12346,18 +12790,25 @@ } } }, - "description": "Attachment conflict." + "description": "" }, "503": { - "description": "Private storage is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/machine-service/requests/{id}/files/presign": { + "/api/v1/admin/events/{id}/image": { "post": { - "operationId": "api_v1_admin_machine_service_requests_files_presign_create", - "summary": "Create a service attachment upload URL", + "operationId": "api_v1_admin_events_image_create", + "summary": "Create an event image upload URL", "parameters": [ { "in": "path", @@ -12369,23 +12820,23 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServiceFilePresign" + "$ref": "#/components/schemas/PublicImageUploadRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ServiceFilePresign" + "$ref": "#/components/schemas/PublicImageUploadRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ServiceFilePresign" + "$ref": "#/components/schemas/PublicImageUploadRequest" } } }, @@ -12401,51 +12852,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServiceFilePresignResponse" + "$ref": "#/components/schemas/PublicImageUploadResponse" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid attachment input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid image upload request." }, "403": { - "description": "Machine management permission required." + "description": "Event management access is required." }, "404": { - "description": "Service request or file was not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Attachment conflict." + "description": "Event not found." }, "503": { - "description": "Private storage is unavailable." + "description": "Public image storage is unavailable." } } - } - }, - "/api/v1/admin/machine-service/requests/{id}/reject": { - "post": { - "operationId": "api_v1_admin_machine_service_requests_reject_create", - "summary": "Reject a machine service request", + }, + "put": { + "operationId": "api_v1_admin_events_image_update", + "summary": "Attach an uploaded image to an event", "parameters": [ { "in": "path", @@ -12457,23 +12886,23 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ServiceReject" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ServiceReject" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ServiceReject" + "$ref": "#/components/schemas/PublicImageAttachRequest" } } }, @@ -12489,48 +12918,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "$ref": "#/components/schemas/EventAdmin" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid service request input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid image object key or size." }, "403": { - "description": "Machine management permission required." + "description": "Event management access is required." }, "404": { - "description": "Service request was not found." + "description": "Event not found." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Service workflow conflict." + "503": { + "description": "Public image storage is unavailable." } } - } - }, - "/api/v1/admin/machine-service/requests/{id}/reprint": { - "post": { - "operationId": "api_v1_admin_machine_service_requests_reprint_create", - "summary": "Create a printer reprint", + }, + "delete": { + "operationId": "api_v1_admin_events_image_destroy", + "summary": "Clear an event image", "parameters": [ { "in": "path", @@ -12542,7 +12952,7 @@ } ], "tags": [ - "Admin machine service" + "Admin events" ], "security": [ { @@ -12554,48 +12964,24 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "$ref": "#/components/schemas/EventAdmin" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid service request input." - }, - "401": { - "description": "Authentication required." - }, "403": { - "description": "Machine management permission required." + "description": "Event management access is required." }, "404": { - "description": "Service request was not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Service workflow conflict." + "description": "Event not found." } } } }, - "/api/v1/admin/machine-service/requests/{id}/start": { - "post": { - "operationId": "api_v1_admin_machine_service_requests_start_create", - "summary": "Start machine service work", + "/api/v1/admin/events/{id}/invites/": { + "get": { + "operationId": "api_v1_admin_events_invites_retrieve", "parameters": [ { "in": "path", @@ -12604,30 +12990,18 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } } ], "tags": [ - "Admin machine service" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ServiceStart" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ServiceStart" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ServiceStart" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -12638,7 +13012,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "$ref": "#/components/schemas/InviteList" } } }, @@ -12648,20 +13022,42 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Invalid service request input." + "description": "Invalid field values, disabled events module or disabled feature." }, "401": { - "description": "Authentication required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Service request was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "409": { "content": { @@ -12671,39 +13067,33 @@ } } }, - "description": "Service workflow conflict." - } - } - } - }, - "/api/v1/admin/machines/{id}": { - "get": { - "operationId": "api_v1_admin_machines_retrieve", - "summary": "Retrieve a machine", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -12711,9 +13101,8 @@ } } }, - "patch": { - "operationId": "api_v1_admin_machines_partial_update", - "summary": "Update a machine", + "post": { + "operationId": "api_v1_admin_events_invites_create", "parameters": [ { "in": "path", @@ -12725,23 +13114,23 @@ } ], "tags": [ - "Admin machines" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedMachine" + "$ref": "#/components/schemas/InviteInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedMachine" + "$ref": "#/components/schemas/InviteInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedMachine" + "$ref": "#/components/schemas/InviteInput" } } } @@ -12752,114 +13141,104 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/Invite" } } }, "description": "" }, "400": { - "description": "Invalid machine details." - } - } - } - }, - "/api/v1/admin/machines/{id}/consumable-candidates": { - "get": { - "operationId": "api_v1_admin_machines_consumable_candidates_list", - "summary": "List inventory products eligible as count consumables", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "Invalid field values, disabled events module or disabled feature." + }, + "401": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ConsumableCandidate" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, "403": { - "description": "Machine operation is not permitted." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Machine not found." - } - } - } - }, - "/api/v1/admin/machines/{id}/consumables": { - "get": { - "operationId": "api_v1_admin_machines_consumables_list", - "summary": "List machine consumables", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "409": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineConsumable" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "403": { - "description": "Machine operation is not permitted." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "404": { - "description": "Machine not found." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } - }, + } + }, + "/api/v1/admin/events/{id}/invites/bulk/": { "post": { - "operationId": "api_v1_admin_machines_consumables_create", - "summary": "Link a count or grams consumable to a machine", + "operationId": "api_v1_admin_events_invites_bulk_create", + "description": "Requires manage_invitations and events.invites. At most 200 email-bound invites; mail contains event navigation and no admission credential.", "parameters": [ { "in": "path", @@ -12871,23 +13250,23 @@ } ], "tags": [ - "Admin machines" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LinkMachineConsumable" + "$ref": "#/components/schemas/BulkInviteInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/LinkMachineConsumable" + "$ref": "#/components/schemas/BulkInviteInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/LinkMachineConsumable" + "$ref": "#/components/schemas/BulkInviteInput" } } }, @@ -12903,83 +13282,101 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineConsumable" + "$ref": "#/components/schemas/BulkInviteResult" } } }, "description": "" }, "400": { - "description": "Invalid consumable." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid field values, disabled events module or disabled feature." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Machine management is not permitted." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Machine not found." - } - } - } - }, - "/api/v1/admin/machines/{id}/consumables/{cid}": { - "delete": { - "operationId": "api_v1_admin_machines_consumables_destroy", - "summary": "Unlink a machine consumable", - "parameters": [ - { - "in": "path", - "name": "cid", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true + "description": "" }, - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" + "description": "" }, - "400": { - "description": "Consumable is not linked." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "403": { - "description": "Machine management is not permitted." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "404": { - "description": "Machine not found." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/machines/{id}/consumables/{cid}/log": { - "post": { - "operationId": "api_v1_admin_machines_consumables_log_create", - "summary": "Log machine consumable usage", + "/api/v1/admin/events/{id}/organizers/": { + "put": { + "operationId": "api_v1_admin_events_organizers_update", + "summary": "Replace an event's organization organizers", "parameters": [ - { - "in": "path", - "name": "cid", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "id", @@ -12990,23 +13387,23 @@ } ], "tags": [ - "Admin machines" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LogMachineConsumption" + "$ref": "#/components/schemas/EventOrganizerReplace" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/LogMachineConsumption" + "$ref": "#/components/schemas/EventOrganizerReplace" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/LogMachineConsumption" + "$ref": "#/components/schemas/EventOrganizerReplace" } } }, @@ -13022,28 +13419,54 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineConsumable" + "$ref": "#/components/schemas/EventOrganizerList" } } }, "description": "" }, "400": { - "description": "Invalid quantity or insufficient stock." + "description": "Invalid or unavailable organization." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Machine operation is not permitted." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Machine not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "description": "Concurrent event state conflict." } } } }, - "/api/v1/admin/machines/{id}/documents": { + "/api/v1/admin/events/{id}/promo-codes/": { "get": { - "operationId": "api_v1_admin_machines_documents_list", - "summary": "List machine documents", + "operationId": "api_v1_admin_events_promo_codes_retrieve", "parameters": [ { "in": "path", @@ -13052,10 +13475,17 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } } ], "tags": [ - "Admin machines" + "Admin events" ], "security": [ { @@ -13067,164 +13497,88 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineDocument" - } + "$ref": "#/components/schemas/PromoList" } } }, "description": "" - } - } - }, - "post": { - "operationId": "api_v1_admin_machines_documents_create", - "summary": "Finalize a machine document upload", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DocumentFinalize" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DocumentFinalize" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DocumentFinalize" - } - } }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineDocument" + "type": "object", + "additionalProperties": {} } } }, - "description": "" + "description": "Invalid field values, disabled events module or disabled feature." }, - "400": { - "description": "Invalid machine document." - }, - "503": { - "description": "Machine document storage is unavailable." - } - } - } - }, - "/api/v1/admin/machines/{id}/documents/presign": { - "post": { - "operationId": "api_v1_admin_machines_documents_presign_create", - "summary": "Create a machine document upload URL", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DocumentPresign" + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DocumentPresign" + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DocumentPresign" + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { - "description": "Machine document upload details." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "400": { - "description": "Invalid document upload request." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "503": { - "description": "Machine document storage is unavailable." - } - } - } - }, - "/api/v1/admin/machines/{id}/error-logs": { - "get": { - "operationId": "api_v1_admin_machines_error_logs_list", - "summary": "List machine error logs", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineErrorLog" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -13233,8 +13587,7 @@ } }, "post": { - "operationId": "api_v1_admin_machines_error_logs_create", - "summary": "Log a machine error", + "operationId": "api_v1_admin_events_promo_codes_create", "parameters": [ { "in": "path", @@ -13246,27 +13599,26 @@ } ], "tags": [ - "Admin machines" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LogError" + "$ref": "#/components/schemas/PromoInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/LogError" + "$ref": "#/components/schemas/PromoInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/LogError" + "$ref": "#/components/schemas/PromoInput" } } - }, - "required": true + } }, "security": [ { @@ -13278,88 +13630,100 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineErrorLog" + "$ref": "#/components/schemas/Promo" } } }, "description": "" }, "400": { - "description": "Invalid error log." - } - } - } - }, - "/api/v1/admin/machines/{id}/image": { - "post": { - "operationId": "api_v1_admin_machines_image_create", - "summary": "Create a machine image upload URL", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "description": "Invalid field values, disabled events module or disabled feature." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Invalid image upload request." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "403": { - "description": "Machine management access is required." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "404": { - "description": "Machine not found." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "503": { - "description": "Public image storage is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } - }, - "put": { - "operationId": "api_v1_admin_machines_image_update", - "summary": "Attach an uploaded image to a machine", + } + }, + "/api/v1/admin/events/{id}/publish/": { + "post": { + "operationId": "api_v1_admin_events_publish_create", + "summary": "Publish an event", "parameters": [ { "in": "path", @@ -13371,27 +13735,26 @@ } ], "tags": [ - "Admin machines" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "$ref": "#/components/schemas/EventWebsitePublishInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "$ref": "#/components/schemas/EventWebsitePublishInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "$ref": "#/components/schemas/EventWebsitePublishInput" } } - }, - "required": true + } }, "security": [ { @@ -13403,71 +13766,70 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/EventAdmin" } } }, "description": "" }, "400": { - "description": "Invalid image object key or size." - }, - "403": { - "description": "Machine management access is required." - }, - "404": { - "description": "Machine not found." - }, - "503": { - "description": "Public image storage is unavailable." - } - } - }, - "delete": { - "operationId": "api_v1_admin_machines_image_destroy", - "summary": "Clear a machine image", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "Invalid event details." + }, + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, "403": { - "description": "Machine management access is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Machine not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/machines/{id}/operator-candidates": { + "/api/v1/admin/events/{id}/registrations/": { "get": { - "operationId": "api_v1_admin_machines_operator_candidates_list", - "summary": "List active members eligible for machine operator assignment", + "operationId": "api_v1_admin_events_registrations_retrieve", + "summary": "List event registrations", "parameters": [ { "in": "path", @@ -13479,7 +13841,7 @@ } ], "tags": [ - "Admin machines" + "Admin events" ], "security": [ { @@ -13491,65 +13853,68 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/OperatorCandidate" - } + "$ref": "#/components/schemas/EventRegistrationListResponse" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Unexpected request body or events module disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required." + }, "403": { - "description": "Operator delegation is not permitted." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management is required." }, "404": { - "description": "Machine not found." - } - } - } - }, - "/api/v1/admin/machines/{id}/operators": { - "get": { - "operationId": "api_v1_admin_machines_operators_list", - "summary": "List machine operators", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "Registration not found." + }, + "409": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineOperator" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Event state or capacity conflict." } } }, "post": { - "operationId": "api_v1_admin_machines_operators_create", - "summary": "Assign a machine operator", + "operationId": "api_v1_admin_events_registrations_create", + "summary": "Register a member for an event", "parameters": [ { "in": "path", @@ -13561,23 +13926,23 @@ } ], "tags": [ - "Admin machines" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AssignOperator" + "$ref": "#/components/schemas/EventStaffRegistration" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/AssignOperator" + "$ref": "#/components/schemas/EventStaffRegistration" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/AssignOperator" + "$ref": "#/components/schemas/EventStaffRegistration" } } }, @@ -13593,22 +13958,68 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineOperator" + "$ref": "#/components/schemas/EventRegistrationAdmin" } } }, "description": "" }, "400": { - "description": "Invalid operator assignment." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid registration." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event state or capacity conflict." } } } }, - "/api/v1/admin/machines/{id}/operators/{user_pk}": { - "patch": { - "operationId": "api_v1_admin_machines_operators_partial_update", - "summary": "Update a machine operator", + "/api/v1/admin/events/{id}/staff-grants/": { + "get": { + "operationId": "api_v1_admin_events_staff_grants_retrieve", "parameters": [ { "in": "path", @@ -13619,36 +14030,16 @@ "required": true }, { - "in": "path", - "name": "user_pk", + "in": "query", + "name": "page", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Admin machines" + "Admin events" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedAssignOperator" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedAssignOperator" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedAssignOperator" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -13659,155 +14050,98 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineOperator" + "$ref": "#/components/schemas/StaffGrantList" } } }, "description": "" }, "400": { - "description": "Invalid operator assignment." - } - } - }, - "delete": { - "operationId": "api_v1_admin_machines_operators_destroy", - "summary": "Remove a machine operator", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true + "description": "" }, - { - "in": "path", - "name": "user_pk", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" - } - } - } - }, - "/api/v1/admin/machines/{id}/publicity": { - "get": { - "operationId": "api_v1_admin_machines_publicity_retrieve", - "summary": "Preview a machine public listing", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicMachine" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - } - } - }, - "patch": { - "operationId": "api_v1_admin_machines_publicity_partial_update", - "summary": "Set machine public visibility", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedMachinePublicity" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedMachinePublicity" + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedMachinePublicity" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicMachine" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Invalid publicity setting." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "403": { - "description": "MANAGE_MACHINES is required." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "404": { - "description": "Machine not found." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/machines/{id}/retire": { - "post": { - "operationId": "api_v1_admin_machines_retire_create", - "summary": "Retire a machine", + "/api/v1/admin/events/{id}/staff-invites/": { + "get": { + "operationId": "api_v1_admin_events_staff_invites_retrieve", "parameters": [ { "in": "path", @@ -13816,10 +14150,17 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } } ], "tags": [ - "Admin machines" + "Admin events" ], "security": [ { @@ -13831,141 +14172,87 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/StaffGrantInviteList" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/machines/{id}/set-status": { - "post": { - "operationId": "api_v1_admin_machines_set_status_create", - "summary": "Set a machine status", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetStatus" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SetStatus" + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SetStatus" + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Invalid machine status." - } - } - } - }, - "/api/v1/admin/machines/{id}/unretire": { - "post": { - "operationId": "api_v1_admin_machines_unretire_create", - "summary": "Reactivate a retired machine", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/machines/{id}/usage": { - "get": { - "operationId": "api_v1_admin_machines_usage_list", - "summary": "List machine usage entries", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "503": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineUsageEntry" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -13974,8 +14261,7 @@ } }, "post": { - "operationId": "api_v1_admin_machines_usage_create", - "summary": "Log machine usage", + "operationId": "api_v1_admin_events_staff_invites_create", "parameters": [ { "in": "path", @@ -13987,23 +14273,23 @@ } ], "tags": [ - "Admin machines" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LogUsage" + "$ref": "#/components/schemas/StaffGrantInviteInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/LogUsage" + "$ref": "#/components/schemas/StaffGrantInviteInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/LogUsage" + "$ref": "#/components/schemas/StaffGrantInviteInput" } } }, @@ -14019,46 +14305,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineUsageEntry" + "$ref": "#/components/schemas/StaffGrantInviteCreated" } } }, "description": "" }, "400": { - "description": "Invalid usage entry." - } - } - } - }, - "/api/v1/admin/machines/{id}/warranty": { - "get": { - "operationId": "api_v1_admin_machines_warranty_retrieve", - "summary": "Retrieve warranty details for a machine", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin warranty" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Warranty" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -14093,64 +14350,18 @@ } }, "description": "" - } - } - }, - "put": { - "operationId": "api_v1_admin_machines_warranty_update", - "summary": "Create or update warranty details for a machine", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin warranty" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WarrantyUpsert" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/WarrantyUpsert" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/WarrantyUpsert" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Warranty" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Invalid warranty details." - }, - "401": { + "423": { "content": { "application/json": { "schema": { @@ -14160,7 +14371,7 @@ }, "description": "" }, - "403": { + "429": { "content": { "application/json": { "schema": { @@ -14170,7 +14381,7 @@ }, "description": "" }, - "404": { + "503": { "content": { "application/json": { "schema": { @@ -14183,10 +14394,9 @@ } } }, - "/api/v1/admin/machines/documents/{id}": { - "delete": { - "operationId": "api_v1_admin_machines_documents_destroy", - "summary": "Delete a machine document", + "/api/v1/admin/events/{id}/ticket-types/": { + "get": { + "operationId": "api_v1_admin_events_ticket_types_retrieve", "parameters": [ { "in": "path", @@ -14195,39 +14405,17 @@ "type": "integer" }, "required": true - } - ], - "tags": [ - "Admin machines" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" - } - } - } - }, - "/api/v1/admin/machines/documents/{id}/url": { - "get": { - "operationId": "api_v1_admin_machines_documents_url_retrieve", - "summary": "Create a signed machine document view URL", - "parameters": [ + }, { - "in": "path", - "name": "id", + "in": "query", + "name": "page", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Admin machines" + "Admin events" ], "security": [ { @@ -14236,41 +14424,27 @@ ], "responses": { "200": { - "description": "Signed machine document URL." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TicketList" + } + } + }, + "description": "" }, - "503": { - "description": "Machine document storage is unavailable." - } - } - } - }, - "/api/v1/admin/maintenance/log-documents/{id}/": { - "delete": { - "operationId": "api_v1_admin_maintenance_log_documents_destroy", - "summary": "Delete a maintenance document", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } }, - "required": true - } - ], - "tags": [ - "Admin maintenance" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" + "description": "Invalid field values, disabled events module or disabled feature." }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -14278,7 +14452,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "403": { "content": { @@ -14288,7 +14462,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -14298,7 +14472,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -14308,7 +14482,27 @@ } } }, - "description": "Workflow conflict." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "503": { "content": { @@ -14318,15 +14512,12 @@ } } }, - "description": "Service unavailable." + "description": "" } } - } - }, - "/api/v1/admin/maintenance/log-documents/{id}/url/": { - "get": { - "operationId": "api_v1_admin_maintenance_log_documents_url_retrieve", - "summary": "Create a private maintenance document URL", + }, + "post": { + "operationId": "api_v1_admin_events_ticket_types_create", "parameters": [ { "in": "path", @@ -14338,25 +14529,56 @@ } ], "tags": [ - "Admin maintenance" + "Admin events" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TicketInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/TicketInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/TicketInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceDocumentUrl" + "$ref": "#/components/schemas/Ticket" } } }, "description": "" }, "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid field values, disabled events module or disabled feature." + }, + "401": { "content": { "application/json": { "schema": { @@ -14364,7 +14586,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "403": { "content": { @@ -14374,7 +14596,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -14384,7 +14606,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -14394,7 +14616,27 @@ } } }, - "description": "Workflow conflict." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "503": { "content": { @@ -14404,15 +14646,14 @@ } } }, - "description": "Service unavailable." + "description": "" } } } }, - "/api/v1/admin/maintenance/logs/{id}/documents/": { - "post": { - "operationId": "api_v1_admin_maintenance_logs_documents_create", - "summary": "Finalize a maintenance document upload", + "/api/v1/admin/events/{id}/website/": { + "get": { + "operationId": "api_v1_admin_events_website_retrieve", "parameters": [ { "in": "path", @@ -14424,39 +14665,19 @@ } ], "tags": [ - "Admin maintenance" + "Event website" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MaintenanceDocumentFinalize" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MaintenanceDocumentFinalize" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MaintenanceDocumentFinalize" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceLogDocument" + "$ref": "#/components/schemas/Website" } } }, @@ -14470,7 +14691,17 @@ } } }, - "description": "Invalid request." + "description": "Invalid content, stale revision, disabled feature or terminal event." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign in required." }, "403": { "content": { @@ -14480,7 +14711,7 @@ } } }, - "description": "Permission denied." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -14490,9 +14721,9 @@ } } }, - "description": "Not found." + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -14500,7 +14731,7 @@ } } }, - "description": "Workflow conflict." + "description": "Rate limit exceeded." }, "503": { "content": { @@ -14510,15 +14741,12 @@ } } }, - "description": "Service unavailable." + "description": "Private image storage is unavailable." } } - } - }, - "/api/v1/admin/maintenance/logs/{id}/documents/presign/": { - "post": { - "operationId": "api_v1_admin_maintenance_logs_documents_presign_create", - "summary": "Create a maintenance document upload URL", + }, + "put": { + "operationId": "api_v1_admin_events_website_update", "parameters": [ { "in": "path", @@ -14530,23 +14758,23 @@ } ], "tags": [ - "Admin maintenance" + "Event website" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceDocumentPresign" + "$ref": "#/components/schemas/WebsiteContentInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MaintenanceDocumentPresign" + "$ref": "#/components/schemas/WebsiteContentInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MaintenanceDocumentPresign" + "$ref": "#/components/schemas/WebsiteContentInput" } } }, @@ -14562,7 +14790,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceDocumentPresignResponse" + "$ref": "#/components/schemas/Website" } } }, @@ -14576,7 +14804,17 @@ } } }, - "description": "Invalid request." + "description": "Invalid content, stale revision, disabled feature or terminal event." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign in required." }, "403": { "content": { @@ -14586,7 +14824,7 @@ } } }, - "description": "Permission denied." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -14596,9 +14834,9 @@ } } }, - "description": "Not found." + "description": "Unknown or out-of-scope event/page/image." }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -14606,7 +14844,7 @@ } } }, - "description": "Workflow conflict." + "description": "Rate limit exceeded." }, "503": { "content": { @@ -14616,15 +14854,14 @@ } } }, - "description": "Service unavailable." + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/maintenance/schedules/{id}/": { - "patch": { - "operationId": "api_v1_admin_maintenance_schedules_partial_update", - "summary": "Update a maintenance schedule", + "/api/v1/admin/events/{id}/website/assets/": { + "post": { + "operationId": "api_v1_admin_events_website_assets_create", "parameters": [ { "in": "path", @@ -14636,26 +14873,17 @@ } ], "tags": [ - "Admin maintenance" + "Event website" ], "requestBody": { "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedMaintenanceScheduleWrite" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedMaintenanceScheduleWrite" - } - }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedMaintenanceScheduleWrite" + "$ref": "#/components/schemas/WebsiteAssetInput" } } - } + }, + "required": true }, "security": [ { @@ -14663,42 +14891,91 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceSchedule" + "$ref": "#/components/schemas/WebsiteAsset" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid content, stale revision, disabled feature or terminal event." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign in required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Missing event permission or wrong staff origin." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown or out-of-scope event/page/image." }, - "409": { - "description": "Conflicts with the current state." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/maintenance/schedules/{id}/deactivate/": { - "post": { - "operationId": "api_v1_admin_maintenance_schedules_deactivate_create", - "summary": "Deactivate a maintenance schedule", + "/api/v1/admin/events/{id}/website/assets/{asset_id}": { + "get": { + "operationId": "api_v1_admin_events_website_assets_retrieve", "parameters": [ + { + "in": "path", + "name": "asset_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "id", @@ -14709,7 +14986,7 @@ } ], "tags": [ - "Admin maintenance" + "Event website" ], "security": [ { @@ -14721,87 +14998,97 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceSchedule" + "type": "string", + "format": "binary" } } }, - "description": "" + "description": "Private WebP image; no-store." }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid content, stale revision, disabled feature or terminal event." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign in required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Missing event permission or wrong staff origin." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown or out-of-scope event/page/image." }, - "409": { - "description": "Conflicts with the current state." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/accepted-requests": { - "get": { - "operationId": "api_v1_admin_makerspace_accepted_requests_list", - "summary": "List accepted requests awaiting issue", + }, + "delete": { + "operationId": "api_v1_admin_events_website_assets_destroy", "parameters": [ - { - "name": "id", - "in": "query", - "required": false, - "description": "Select one result within the authorized list.", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 9223372036854775807 - } - }, { "in": "path", - "name": "makerspace_id", + "name": "asset_id", "schema": { "type": "integer" }, "required": true }, { - "name": "ordering", - "required": false, - "in": "query", - "description": "Which field to use when ordering the results.", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", + "in": "path", + "name": "id", "schema": { "type": "integer" - } - }, - { - "name": "search", - "required": false, - "in": "query", - "description": "A search term (requested-for, requester name/email).", - "schema": { - "type": "string" - } + }, + "required": true } ], "tags": [ - "Admin requests" + "Event website" ], "security": [ { @@ -14809,17 +15096,20 @@ } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedAdminRequestList" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Invalid content, stale revision, disabled feature or terminal event." }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -14827,9 +15117,9 @@ } } }, - "description": "Invalid request." + "description": "Sign in required." }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -14837,9 +15127,9 @@ } } }, - "description": "Authentication required." + "description": "Missing event permission or wrong staff origin." }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -14847,9 +15137,9 @@ } } }, - "description": "Permission denied." + "description": "Unknown or out-of-scope event/page/image." }, - "404": { + "429": { "content": { "application/json": { "schema": { @@ -14857,19 +15147,28 @@ } } }, - "description": "Not found." + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/accountability": { - "get": { - "operationId": "api_v1_admin_makerspace_accountability_retrieve", - "summary": "Requester accountability dashboard", + "/api/v1/admin/events/{id}/website/release/": { + "post": { + "operationId": "api_v1_admin_events_website_release_create", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -14877,8 +15176,28 @@ } ], "tags": [ - "Analytics" + "Event website" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteReleaseInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WebsiteReleaseInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WebsiteReleaseInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -14889,8 +15208,7 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/Website" } } }, @@ -14900,100 +15218,109 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Invalid content, stale revision, disabled feature or terminal event." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "Sign in required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Unknown or out-of-scope event/page/image." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/active-loans": { - "get": { - "operationId": "api_v1_admin_makerspace_active_loans_list", - "summary": "List active loans awaiting return", + "/api/v1/admin/events/{id}/website/sessions/{slug}/delay/": { + "post": { + "operationId": "api_v1_admin_events_website_sessions_delay_create", "parameters": [ { + "in": "path", "name": "id", - "in": "query", - "required": false, - "description": "Select one result within the authorized list.", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 9223372036854775807 - } - }, - { - "in": "path", - "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "name": "ordering", - "required": false, - "in": "query", - "description": "Which field to use when ordering the results.", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } - }, - { - "name": "search", - "required": false, - "in": "query", - "description": "A search term (requested-for, requester name/email).", + "in": "path", + "name": "slug", "schema": { "type": "string" - } + }, + "required": true } ], "tags": [ - "Admin requests" + "Event website" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteDelayInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WebsiteDelayInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WebsiteDelayInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -15004,7 +15331,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedAdminRequestList" + "$ref": "#/components/schemas/Website" } } }, @@ -15018,7 +15345,7 @@ } } }, - "description": "Invalid request." + "description": "Invalid content, stale revision, disabled feature or terminal event." }, "401": { "content": { @@ -15028,7 +15355,7 @@ } } }, - "description": "Authentication required." + "description": "Sign in required." }, "403": { "content": { @@ -15038,7 +15365,7 @@ } } }, - "description": "Permission denied." + "description": "Missing event permission or wrong staff origin." }, "404": { "content": { @@ -15048,109 +15375,82 @@ } } }, - "description": "Not found." + "description": "Unknown or out-of-scope event/page/image." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Private image storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/{report_key}": { + "/api/v1/admin/evidence/{id}": { "get": { - "operationId": "api_v1_admin_makerspace_analytics_retrieve", - "summary": "Get analytics report", + "operationId": "api_v1_admin_evidence_retrieve", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, + } + ], + "tags": [ + "api" + ], + "security": [ { - "in": "query", - "name": "months", - "schema": { - "type": "integer" + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvidenceGetResponse" + } + } }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, + "description": "" + } + } + } + }, + "/api/v1/admin/inventory/{id}": { + "get": { + "operationId": "api_v1_admin_inventory_retrieve", + "summary": "Retrieve or update inventory product", + "parameters": [ { "in": "path", - "name": "report_key", + "name": "id", "schema": { - "type": "string" + "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin inventory" ], "security": [ { @@ -15162,146 +15462,61 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/InventoryProductAdmin" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Invalid report request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Makerspace or report not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/active-loans": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_active_loans_retrieve", - "summary": "Get analytics report", + }, + "patch": { + "operationId": "api_v1_admin_inventory_partial_update", + "summary": "Retrieve or update inventory product", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin inventory" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedInventoryProductAdminUpdate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedInventoryProductAdminUpdate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedInventoryProductAdminUpdate" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -15312,90 +15527,94 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/InventoryProductAdminUpdate" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Invalid report request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + } + } + } + }, + "/api/v1/admin/inventory/{id}/adjust-quantity": { + "post": { + "operationId": "api_v1_admin_inventory_adjust_quantity_create", + "summary": "Adjust inventory quantity buckets", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin inventory" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryQuantityAdjustment" } }, - "description": "Permission denied." + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/InventoryQuantityAdjustment" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/InventoryQuantityAdjustment" + } + } }, - "404": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/InventoryProductAdmin" } } }, - "description": "Makerspace or report not found." + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/booking-utilization": { + "/api/v1/admin/inventory/{id}/chain-of-custody": { "get": { - "operationId": "api_v1_admin_makerspace_analytics_booking_utilization_retrieve", - "summary": "Get analytics report", + "operationId": "api_v1_admin_inventory_chain_of_custody_retrieve", + "summary": "Read-only chain of custody for one inventory item", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -15403,54 +15622,15 @@ }, { "in": "query", - "name": "months", + "name": "limit", "schema": { "type": "integer" }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } + "description": "Maximum history events to return. Defaults to 200; capped at 500." } ], "tags": [ - "Analytics" + "Admin inventory" ], "security": [ { @@ -15462,7 +15642,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/InventoryChainOfCustodyResponse" } } }, @@ -15472,17 +15652,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -15492,7 +15672,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -15502,105 +15682,161 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." - } + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/damaged-lost": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_damaged_lost_retrieve", - "summary": "Get analytics report", + "/api/v1/admin/inventory/{id}/image": { + "post": { + "operationId": "api_v1_admin_inventory_image_create", + "summary": "Create an inventory product image upload URL", "parameters": [ { - "in": "query", - "name": "end", + "in": "path", + "name": "id", "schema": { - "type": "string", - "format": "date" + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin inventory" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } } }, + "required": true + }, + "security": [ { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadResponse" + } + } + }, + "description": "" }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } + "400": { + "description": "Invalid image upload request." }, + "503": { + "description": "Public image storage is unavailable." + } + } + }, + "put": { + "operationId": "api_v1_admin_inventory_image_update", + "summary": "Attach an uploaded image to an inventory product", + "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" + } + ], + "tags": [ + "Admin inventory" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicImageAttachRequest" + } }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicImageAttachRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicImageAttachRequest" + } } }, + "required": true + }, + "security": [ { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InventoryProductAdmin" + } + } + }, + "description": "" + }, + "400": { + "description": "Invalid image object key or size." }, + "503": { + "description": "Public image storage is unavailable." + } + } + }, + "delete": { + "operationId": "api_v1_admin_inventory_image_destroy", + "summary": "Clear an inventory product image", + "parameters": [ { - "in": "query", - "name": "subject_type", + "in": "path", + "name": "id", "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } + "type": "integer" + }, + "required": true } ], "tags": [ - "Analytics" + "Admin inventory" ], "security": [ { @@ -15612,145 +15848,261 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/InventoryProductAdmin" } } }, "description": "" - }, - "400": { + } + } + } + }, + "/api/v1/admin/inventory/{id}/lending-history": { + "get": { + "operationId": "api_v1_admin_inventory_lending_history_retrieve", + "summary": "Per-item lending history (last borrower + last 3 lends)", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin inventory" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/LendingHistoryResponse" } } }, - "description": "Invalid report request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } + "description": "" + } + } + } + }, + "/api/v1/admin/inventory/{id}/needs-fix": { + "post": { + "operationId": "api_v1_admin_inventory_needs_fix_create", + "description": "Move units onto the shelf, back to available, or out of inventory.", + "summary": "Move inventory units to, from, or out of the to-be-fixed shelf", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin inventory" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NeedsFixAction" } }, - "description": "Authentication required." + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/NeedsFixAction" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/NeedsFixAction" + } + } }, - "403": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/InventoryProductAdmin" } } }, - "description": "Permission denied." - }, - "404": { + "description": "" + } + } + } + }, + "/api/v1/admin/inventory/{id}/qr-history": { + "get": { + "operationId": "api_v1_admin_inventory_qr_history_retrieve", + "summary": "List QR scan history for an inventory product", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin inventory" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/ProductQrHistory" } } }, - "description": "Makerspace or report not found." + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/damaged-missing": { + "/api/v1/admin/inventory/{product_pk}/assets": { "get": { - "operationId": "api_v1_admin_makerspace_analytics_damaged_missing_retrieve", - "summary": "Get analytics report", + "operationId": "api_v1_admin_inventory_assets_list", + "summary": "List individual assets for an inventory product", "parameters": [ { + "name": "page", + "required": false, "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", + "description": "A page number within the paginated result set.", "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] + "type": "integer" } }, { + "name": "page_size", + "required": false, "in": "query", - "name": "limit", + "description": "Number of results to return per page.", "schema": { "type": "integer" } }, { "in": "path", - "name": "makerspace_id", + "name": "product_pk", "schema": { "type": "integer" }, "required": true - }, + } + ], + "tags": [ + "Admin inventory" + ], + "security": [ { - "in": "query", - "name": "months", - "schema": { - "type": "integer" + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedInventoryAssetAdminList" + } + } }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, + "description": "" + } + } + } + }, + "/api/v1/admin/inventory/needs-fix": { + "get": { + "operationId": "api_v1_admin_inventory_needs_fix_list", + "description": "The to-be-fixed shelf: products that currently have units awaiting repair.", + "parameters": [ { + "name": "page", + "required": false, "in": "query", - "name": "start", + "description": "A page number within the paginated result set.", "schema": { - "type": "string", - "format": "date" + "type": "integer" } }, { + "name": "page_size", + "required": false, "in": "query", - "name": "status", + "description": "Number of results to return per page.", "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] + "type": "integer" } - }, + } + ], + "tags": [ + "api" + ], + "security": [ { - "in": "query", - "name": "subject_type", + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedInventoryProductAdminList" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/admin/invitation-requests/{id}/decline": { + "post": { + "operationId": "api_v1_admin_invitation_requests_decline_create", + "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", + "summary": "Decline an invitation request", + "parameters": [ + { + "in": "path", + "name": "id", "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } + "type": "integer" + }, + "required": true } ], "tags": [ - "Analytics" + "Admin memberships" ], "security": [ { @@ -15762,7 +16114,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/InvitationRequest" } } }, @@ -15772,136 +16124,93 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/event-attendance": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_event_attendance_retrieve", - "summary": "Get analytics report", + "/api/v1/admin/invitation-requests/{id}/invite": { + "post": { + "operationId": "api_v1_admin_invitation_requests_invite_create", + "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", + "summary": "Turn an invitation request into a membership invitation", "parameters": [ { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, - { - "in": "path", - "name": "makerspace_id", + "in": "path", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RoleId" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/RoleId" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RoleId" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -15912,7 +16221,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/InvitationRequest" } } }, @@ -15922,135 +16231,72 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/fablab-health": { + "/api/v1/admin/kiosk-replays/{id}/": { "get": { - "operationId": "api_v1_admin_makerspace_analytics_fablab_health_retrieve", - "summary": "Get analytics report", + "operationId": "api_v1_admin_kiosk_replays_retrieve", + "summary": "Inspect one current-action-scoped failed handover", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin kiosk recovery" ], "security": [ { @@ -16062,7 +16308,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/ReplayRecovery" } } }, @@ -16072,17 +16318,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Invalid filter/resolution or disabled required module." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -16092,116 +16338,84 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "Account, current action or origin refused." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Failure is unavailable in your scope." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Resolution/replacement conflict." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant source gate closed." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/machine-usage": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_machine_usage_retrieve", - "summary": "Get analytics report", + "/api/v1/admin/kiosk-replays/{id}/resolve/": { + "post": { + "operationId": "api_v1_admin_kiosk_replays_resolve_create", + "description": "Acknowledgement records no physical change. Replacement links only a fresh succeeded replay for the same known subject; this endpoint never executes handovers.", + "summary": "Append an idempotent review of a failed handover", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin kiosk recovery" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReplayResolution" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ReplayResolution" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ReplayResolution" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -16212,7 +16426,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/ReplayRecovery" } } }, @@ -16222,17 +16436,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Invalid filter/resolution or disabled required module." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -16242,115 +16456,61 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "Account, current action or origin refused." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Failure is unavailable in your scope." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Resolution/replacement conflict." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant source gate closed." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/maintenance-activity": { + "/api/v1/admin/kiosk-stations/{id}/": { "get": { - "operationId": "api_v1_admin_makerspace_analytics_maintenance_activity_retrieve", - "summary": "Get analytics report", + "operationId": "api_v1_admin_kiosk_stations_retrieve", + "summary": "Read one kiosk Station without its PIN", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin kiosk stations" ], "security": [ { @@ -16362,7 +16522,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/KioskStation" } } }, @@ -16372,135 +16532,79 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Feature disabled or invalid request." }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "Makerspace access or step-up denied." }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "Station not found." }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Rotate the PIN instead of revealing." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Credential secrets are unavailable." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/member-activity": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_member_activity_retrieve", - "summary": "Get analytics report", + }, + "delete": { + "operationId": "api_v1_admin_kiosk_stations_destroy", + "summary": "Disable a kiosk Station and revoke its leases", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin kiosk stations" ], "security": [ { @@ -16512,7 +16616,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/KioskStation" } } }, @@ -16522,136 +16626,102 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Feature disabled or invalid request." }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "Makerspace access or step-up denied." }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "Station not found." }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Rotate the PIN instead of revealing." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Credential secrets are unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/most-lent": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_most_lent_retrieve", - "summary": "Get analytics report", + "/api/v1/admin/kiosk-stations/{id}/reveal/": { + "post": { + "operationId": "api_v1_admin_kiosk_stations_reveal_create", + "summary": "Reveal a kiosk Station PIN after password step-up", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin kiosk stations" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KioskStationReveal" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/KioskStationReveal" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/KioskStationReveal" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -16662,7 +16732,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/KioskStationRevealResponse" } } }, @@ -16672,135 +16742,81 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Feature disabled or invalid request." }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "Makerspace access or step-up denied." }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "Station not found." }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Rotate the PIN instead of revealing." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Credential secrets are unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/payment-reconciliation": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_payment_reconciliation_retrieve", - "summary": "Get analytics report", + "/api/v1/admin/kiosk-stations/{id}/rotate/": { + "post": { + "operationId": "api_v1_admin_kiosk_stations_rotate_create", + "summary": "Rotate a kiosk Station PIN (re-enables it)", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin kiosk stations" ], "security": [ { @@ -16812,7 +16828,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/KioskStationSecret" } } }, @@ -16822,135 +16838,143 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Feature disabled or invalid request." }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "Makerspace access or step-up denied." }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "Station not found." }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Rotate the PIN instead of revealing." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Credential secrets are unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/qr-scans": { + "/api/v1/admin/ledger": { "get": { - "operationId": "api_v1_admin_makerspace_analytics_qr_scans_retrieve", - "summary": "Get analytics report", + "operationId": "api_v1_admin_ledger_retrieve", + "summary": "List outstanding inventory loans across all makerspaces", "parameters": [ { "in": "query", - "name": "end", + "name": "makerspace", "schema": { - "type": "string", - "format": "date" + "type": "integer" } }, { "in": "query", - "name": "grain", + "name": "overdue", "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] + "type": "boolean" } }, { "in": "query", - "name": "limit", + "name": "page", "schema": { "type": "integer" } }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "query", - "name": "months", + "name": "page_size", "schema": { "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + } }, { "in": "query", - "name": "start", + "name": "search", "schema": { - "type": "string", - "format": "date" + "type": "string" } }, { "in": "query", - "name": "status", + "name": "sort", "schema": { "type": "string", "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" + "-due", + "-holder", + "-item_name", + "-makerspace_id", + "-quantity", + "-since", + "-source", + "due", + "holder", + "item_name", + "makerspace_id", + "quantity", + "since", + "source" ] } }, { "in": "query", - "name": "subject_type", + "name": "source", "schema": { "type": "string", "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" + "direct", + "reviewed", + "self_checkout" ] } } ], "tags": [ - "Analytics" + "Ledger" ], "security": [ { @@ -16962,145 +16986,90 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/LedgerResponse" } } }, "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Invalid report request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Permission denied." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Makerspace or report not found." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/recently-added": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_recently_added_retrieve", - "summary": "Get analytics report", - "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } + } + } + } + }, + "/api/v1/admin/ledger/export": { + "get": { + "operationId": "api_v1_admin_ledger_export_retrieve", + "summary": "Export outstanding inventory loans across all makerspaces", + "parameters": [ + { + "in": "query", + "name": "format", + "schema": { + "type": "string", + "enum": [ + "csv", + "xlsx" + ] + } }, { "in": "query", - "name": "limit", + "name": "makerspace", "schema": { "type": "integer" } }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "query", - "name": "months", + "name": "overdue", "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + "type": "boolean" + } }, { "in": "query", - "name": "start", + "name": "search", "schema": { - "type": "string", - "format": "date" + "type": "string" } }, { "in": "query", - "name": "status", + "name": "sort", "schema": { "type": "string", "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" + "-due", + "-holder", + "-item_name", + "-makerspace_id", + "-quantity", + "-since", + "-source", + "due", + "holder", + "item_name", + "makerspace_id", + "quantity", + "since", + "source" ] } }, { "in": "query", - "name": "subject_type", + "name": "source", "schema": { "type": "string", "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" + "direct", + "reviewed", + "self_checkout" ] } } ], "tags": [ - "Analytics" + "Ledger" ], "security": [ { @@ -17110,61 +17079,27 @@ "responses": { "200": { "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Invalid report request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { + "text/csv": { "schema": { - "$ref": "#/components/schemas/ReportError" + "type": "string" } - } - }, - "description": "Permission denied." - }, - "404": { - "content": { - "application/json": { + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { "schema": { - "$ref": "#/components/schemas/ReportError" + "type": "string", + "format": "binary" } } }, - "description": "Makerspace or report not found." + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/returns": { + "/api/v1/admin/machine-service-report": { "get": { - "operationId": "api_v1_admin_makerspace_analytics_returns_retrieve", - "summary": "Get analytics report", + "operationId": "api_v1_admin_machine_service_report_retrieve", + "summary": "Retrieve aggregate machine-service report", "parameters": [ { "in": "query", @@ -17176,39 +17111,11 @@ }, { "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", + "name": "machine_type", "schema": { - "type": "integer" + "type": "string" } }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, { "in": "query", "name": "start", @@ -17216,41 +17123,10 @@ "type": "string", "format": "date" } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin machine service" ], "security": [ { @@ -17262,7 +17138,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/MachineServiceReportResponse" } } }, @@ -17272,136 +17148,147 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Invalid request." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Authentication required." + "description": "Authentication credentials were not provided." }, "403": { + "description": "Machine management permission required." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/admin/machine-service/consumable-pools/{id}": { + "get": { + "operationId": "api_v1_admin_machine_service_consumable_pools_retrieve", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machine service" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/PrinterPool" } } }, - "description": "Permission denied." - }, - "404": { + "description": "" + } + } + }, + "patch": { + "operationId": "api_v1_admin_machine_service_consumable_pools_partial_update", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machine service" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedPrinterPoolVisibility" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedPrinterPoolVisibility" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedPrinterPoolVisibility" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/PrinterPool" } } }, - "description": "Makerspace or report not found." + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/summary": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_summary_retrieve", - "summary": "Get analytics report", + "/api/v1/admin/machine-service/consumable-pools/{id}/adjustments": { + "post": { + "operationId": "api_v1_admin_machine_service_consumable_pools_adjustments_create", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin machine service" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PrinterPoolCorrection" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PrinterPoolCorrection" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PrinterPoolCorrection" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -17412,145 +17299,92 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/PrinterPool" } } }, "description": "" + } + } + } + }, + "/api/v1/admin/machine-service/files/{id}": { + "delete": { + "operationId": "api_v1_admin_machine_service_files_destroy", + "summary": "Delete a staged service attachment", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machine service" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" }, "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Invalid attachment input." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, "description": "Authentication required." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Permission denied." + "description": "Machine management permission required." }, "404": { + "description": "Service request or file was not found." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Attachment conflict." + }, + "503": { + "description": "Private storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/taken-items": { + "/api/v1/admin/machine-service/files/{id}/url": { "get": { - "operationId": "api_v1_admin_makerspace_analytics_taken_items_retrieve", - "summary": "Get analytics report", + "operationId": "api_v1_admin_machine_service_files_url_retrieve", + "summary": "Create a signed service attachment URL", "parameters": [ { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, - { - "in": "path", - "name": "makerspace_id", + "in": "path", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin machine service" ], "security": [ { @@ -17562,7 +17396,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/ServiceFileUrl" } } }, @@ -17572,136 +17406,74 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Invalid attachment input." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, "description": "Authentication required." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Permission denied." + "description": "Machine management permission required." }, "404": { + "description": "Service request or file was not found." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Attachment conflict." + }, + "503": { + "description": "Private storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/analytics/top-borrowers": { - "get": { - "operationId": "api_v1_admin_makerspace_analytics_top_borrowers_retrieve", - "summary": "Get analytics report", + "/api/v1/admin/machine-service/payments/{id}/mark-offline": { + "post": { + "operationId": "api_v1_admin_machine_service_payments_mark_offline_create", + "summary": "Mark a machine-service payment paid offline", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Payments" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentOffline" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PaymentOffline" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PaymentOffline" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -17712,7 +17484,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AnalyticsReportResponse" + "$ref": "#/components/schemas/StaffPayment" } } }, @@ -17722,17 +17494,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "Missing or invalid settlement receipt." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -17742,7 +17514,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -17752,23 +17524,33 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "Payment not found or out of machine scope." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "`payment_terminal`: already reconciled." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/api-client-scopes": { - "get": { - "operationId": "api_v1_admin_makerspace_api_client_scopes_retrieve", - "summary": "List API-client scope grant options", + "/api/v1/admin/machine-service/payments/{id}/waive": { + "post": { + "operationId": "api_v1_admin_machine_service_payments_waive_create", + "summary": "Waive a machine-service payment", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -17776,7 +17558,7 @@ } ], "tags": [ - "API clients" + "Payments" ], "security": [ { @@ -17788,7 +17570,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApiClientScopeCatalogResponse" + "$ref": "#/components/schemas/StaffPayment" } } }, @@ -17802,7 +17584,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -17812,7 +17594,7 @@ } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { @@ -17822,36 +17604,37 @@ } } }, - "description": "" + "description": "Payment not found or out of machine scope." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "`payment_terminal`: already reconciled." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/api-clients": { + "/api/v1/admin/machine-service/requests/{id}": { "get": { - "operationId": "api_v1_admin_makerspace_api_clients_list", - "summary": "List or create makerspace API clients", + "operationId": "api_v1_admin_machine_service_requests_retrieve", + "summary": "Retrieve a machine service request", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "API clients" + "Admin machine service" ], "security": [ { @@ -17863,13 +17646,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedApiClientList" + "$ref": "#/components/schemas/MachineServiceRequest" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -17877,19 +17660,18 @@ } } }, - "description": "" + "description": "Invalid service request input." + }, + "401": { + "description": "Authentication required." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Machine management permission required." }, "404": { + "description": "Service request was not found." + }, + "409": { "content": { "application/json": { "schema": { @@ -17897,17 +17679,19 @@ } } }, - "description": "" + "description": "Service workflow conflict." } } - }, + } + }, + "/api/v1/admin/machine-service/requests/{id}/accept": { "post": { - "operationId": "api_v1_admin_makerspace_api_clients_create", - "summary": "List or create makerspace API clients", + "operationId": "api_v1_admin_machine_service_requests_accept_create", + "summary": "Accept a machine service request", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -17915,27 +17699,26 @@ } ], "tags": [ - "API clients" + "Admin machine service" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApiClient" + "$ref": "#/components/schemas/ServiceAccept" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ApiClient" + "$ref": "#/components/schemas/ServiceAccept" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ApiClient" + "$ref": "#/components/schemas/ServiceAccept" } } - }, - "required": true + } }, "security": [ { @@ -17943,17 +17726,17 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApiClientCreateResponse" + "$ref": "#/components/schemas/MachineServiceRequest" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -17961,19 +17744,18 @@ } } }, - "description": "" + "description": "Invalid service request input." + }, + "401": { + "description": "Authentication required." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Machine management permission required." }, "404": { + "description": "Service request was not found." + }, + "409": { "content": { "application/json": { "schema": { @@ -17981,19 +17763,19 @@ } } }, - "description": "" + "description": "Service workflow conflict." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/api-settings": { - "get": { - "operationId": "api_v1_admin_makerspace_api_settings_retrieve", - "summary": "Retrieve API integration settings", + "/api/v1/admin/machine-service/requests/{id}/collect": { + "post": { + "operationId": "api_v1_admin_machine_service_requests_collect_create", + "summary": "Mark a machine service request collected", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -18001,7 +17783,7 @@ } ], "tags": [ - "API clients" + "Admin machine service" ], "security": [ { @@ -18013,30 +17795,52 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApiIntegrationSettings" + "$ref": "#/components/schemas/MachineServiceRequest" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid service request input." + }, "401": { "description": "Authentication required." }, "403": { - "description": "The actor lacks manage_makerspace here." + "description": "Machine management permission required." }, "404": { - "description": "Makerspace not found, archived, not active, or outside the actor's scope." + "description": "Service request was not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service workflow conflict." } } - }, - "patch": { - "operationId": "api_v1_admin_makerspace_api_settings_partial_update", - "summary": "Update API integration settings", + } + }, + "/api/v1/admin/machine-service/requests/{id}/complete": { + "post": { + "operationId": "api_v1_admin_machine_service_requests_complete_create", + "summary": "Complete machine service work", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -18044,26 +17848,27 @@ } ], "tags": [ - "API clients" + "Admin machine service" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedApiIntegrationSettings" + "$ref": "#/components/schemas/ServiceComplete" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedApiIntegrationSettings" + "$ref": "#/components/schemas/ServiceComplete" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedApiIntegrationSettings" + "$ref": "#/components/schemas/ServiceComplete" } } - } + }, + "required": true }, "security": [ { @@ -18075,35 +17880,52 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApiIntegrationSettings" + "$ref": "#/components/schemas/MachineServiceRequest" } } }, "description": "" }, "400": { - "description": "Field validation errors, keyed by field." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid service request input." }, "401": { "description": "Authentication required." }, "403": { - "description": "The actor lacks manage_makerspace here." + "description": "Machine management permission required." }, "404": { - "description": "Makerspace not found, archived, not active, or outside the actor's scope." + "description": "Service request was not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service workflow conflict." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients": { - "get": { - "operationId": "api_v1_admin_makerspace_archive_recipients_list", - "summary": "List archive recipients for a makerspace", + "/api/v1/admin/machine-service/requests/{id}/fail": { + "post": { + "operationId": "api_v1_admin_machine_service_requests_fail_create", + "summary": "Mark machine service work failed", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -18111,8 +17933,28 @@ } ], "tags": [ - "Backup recipients" + "Admin machine service" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceFail" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ServiceFail" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ServiceFail" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -18123,10 +17965,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ArchiveRecipient" - } + "$ref": "#/components/schemas/MachineServiceRequest" } } }, @@ -18136,52 +17975,42 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient or lifecycle request is invalid." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The recipient does not exist in this makerspace." + "description": "Invalid service request input." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Authentication is required." + "description": "Authentication required." }, "403": { + "description": "Machine management permission required." + }, + "404": { + "description": "Service request was not found." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Manage-makerspace permission is required." + "description": "Service workflow conflict." } } - }, + } + }, + "/api/v1/admin/machine-service/requests/{id}/files/finalize": { "post": { - "operationId": "api_v1_admin_makerspace_archive_recipients_create", - "description": "The decrypted challenge is a 32-byte nonce exchanged as canonical, unpadded base64url. Only the SHA-256 digest of the raw 32 nonce bytes is persisted.", - "summary": "Enroll an archive recipient and issue a custody challenge", + "operationId": "api_v1_admin_machine_service_requests_files_finalize_create", + "summary": "Finalize a service attachment upload", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -18189,23 +18018,23 @@ } ], "tags": [ - "Backup recipients" + "Admin machine service" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientCreate" + "$ref": "#/components/schemas/ServiceFileFinalize" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientCreate" + "$ref": "#/components/schemas/ServiceFileFinalize" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientCreate" + "$ref": "#/components/schemas/ServiceFileFinalize" } } }, @@ -18221,7 +18050,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientChallenge" + "$ref": "#/components/schemas/ServiceFileFinalizeResponse" } } }, @@ -18231,69 +18060,41 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The recipient or lifecycle request is invalid." + "description": "Invalid attachment input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "Machine management permission required." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient does not exist in this makerspace." + "description": "Service request or file was not found." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The recipient or lifecycle request is invalid." + "description": "Attachment conflict." }, "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The age challenge could not be encrypted." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Authentication is required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Manage-makerspace permission is required." + "description": "Private storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/compromise": { + "/api/v1/admin/machine-service/requests/{id}/files/presign": { "post": { - "operationId": "api_v1_admin_makerspace_archive_recipients_compromise_create", - "summary": "Mark an archive recipient as compromised", + "operationId": "api_v1_admin_machine_service_requests_files_presign_create", + "summary": "Create a service attachment upload URL", "parameters": [ { "in": "path", @@ -18302,30 +18103,42 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Backup recipients" + "Admin machine service" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceFilePresign" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ServiceFilePresign" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ServiceFilePresign" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipient" + "$ref": "#/components/schemas/ServiceFilePresignResponse" } } }, @@ -18335,49 +18148,41 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient or lifecycle request is invalid." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The recipient does not exist in this makerspace." + "description": "Invalid attachment input." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Authentication is required." + "description": "Authentication required." }, "403": { + "description": "Machine management permission required." + }, + "404": { + "description": "Service request or file was not found." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Manage-makerspace permission is required." + "description": "Attachment conflict." + }, + "503": { + "description": "Private storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/reactivate": { + "/api/v1/admin/machine-service/requests/{id}/reject": { "post": { - "operationId": "api_v1_admin_makerspace_archive_recipients_reactivate_create", - "summary": "Reactivate a revoked archive recipient", + "operationId": "api_v1_admin_machine_service_requests_reject_create", + "summary": "Reject a machine service request", "parameters": [ { "in": "path", @@ -18386,19 +18191,31 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Backup recipients" + "Admin machine service" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceReject" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ServiceReject" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ServiceReject" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -18409,7 +18226,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipient" + "$ref": "#/components/schemas/MachineServiceRequest" } } }, @@ -18419,50 +18236,38 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient or lifecycle request is invalid." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The recipient does not exist in this makerspace." + "description": "Invalid service request input." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Authentication is required." + "description": "Authentication required." }, "403": { + "description": "Machine management permission required." + }, + "404": { + "description": "Service request was not found." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Manage-makerspace permission is required." + "description": "Service workflow conflict." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/reissue-challenge": { + "/api/v1/admin/machine-service/requests/{id}/reprint": { "post": { - "operationId": "api_v1_admin_makerspace_archive_recipients_reissue_challenge_create", - "description": "The decrypted 32-byte nonce is exchanged as canonical, unpadded base64url. The prior challenge stops verifying when this request commits.", - "summary": "Reissue an archive-recipient custody challenge", + "operationId": "api_v1_admin_machine_service_requests_reprint_create", + "summary": "Create a printer reprint", "parameters": [ { "in": "path", @@ -18471,18 +18276,10 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Backup recipients" + "Admin machine service" ], "security": [ { @@ -18494,7 +18291,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientChallenge" + "$ref": "#/components/schemas/MachineServiceRequest" } } }, @@ -18504,59 +18301,38 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient or lifecycle request is invalid." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient does not exist in this makerspace." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The age challenge could not be encrypted." + "description": "Invalid service request input." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Authentication is required." + "description": "Authentication required." }, "403": { + "description": "Machine management permission required." + }, + "404": { + "description": "Service request was not found." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Manage-makerspace permission is required." + "description": "Service workflow conflict." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/revoke": { + "/api/v1/admin/machine-service/requests/{id}/start": { "post": { - "operationId": "api_v1_admin_makerspace_archive_recipients_revoke_create", - "summary": "Revoke an archive recipient", + "operationId": "api_v1_admin_machine_service_requests_start_create", + "summary": "Start machine service work", "parameters": [ { "in": "path", @@ -18565,19 +18341,30 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Backup recipients" + "Admin machine service" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceStart" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ServiceStart" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ServiceStart" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -18588,7 +18375,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipient" + "$ref": "#/components/schemas/MachineServiceRequest" } } }, @@ -18598,50 +18385,38 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient or lifecycle request is invalid." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The recipient does not exist in this makerspace." + "description": "Invalid service request input." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Authentication is required." + "description": "Authentication required." }, "403": { + "description": "Machine management permission required." + }, + "404": { + "description": "Service request was not found." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Manage-makerspace permission is required." + "description": "Service workflow conflict." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/verify": { - "post": { - "operationId": "api_v1_admin_makerspace_archive_recipients_verify_create", - "description": "Submit the decrypted nonce as canonical unpadded base64url. It must decode to exactly 32 bytes; padding and non-canonical forms are refused.", - "summary": "Verify possession of an archive recipient", + "/api/v1/admin/machines/{id}": { + "get": { + "operationId": "api_v1_admin_machines_retrieve", + "summary": "Retrieve a machine", "parameters": [ { "in": "path", @@ -18650,10 +18425,36 @@ "type": "integer" }, "required": true - }, + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Machine" + } + } + }, + "description": "" + } + } + }, + "patch": { + "operationId": "api_v1_admin_machines_partial_update", + "summary": "Update a machine", + "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -18661,27 +18462,26 @@ } ], "tags": [ - "Backup recipients" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientVerify" + "$ref": "#/components/schemas/PatchedMachine" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientVerify" + "$ref": "#/components/schemas/PatchedMachine" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipientVerify" + "$ref": "#/components/schemas/PatchedMachine" } } - }, - "required": true + } }, "security": [ { @@ -18693,83 +18493,26 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ArchiveRecipient" + "$ref": "#/components/schemas/Machine" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient or lifecycle request is invalid." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient or lifecycle request is invalid." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "The recipient does not exist in this makerspace." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Verification attempts for this recipient are throttled." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Authentication is required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRecipientError" - } - } - }, - "description": "Manage-makerspace permission is required." + "description": "Invalid machine details." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/archive-requests": { + "/api/v1/admin/machines/{id}/consumable-candidates": { "get": { - "operationId": "api_v1_admin_makerspace_archive_requests_list", - "summary": "List archive requests for a makerspace", + "operationId": "api_v1_admin_machines_consumable_candidates_list", + "summary": "List inventory products eligible as count consumables", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -18777,7 +18520,7 @@ } ], "tags": [ - "Admin makerspaces" + "Admin machines" ], "security": [ { @@ -18791,7 +18534,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/MakerspaceArchiveRequest" + "$ref": "#/components/schemas/ConsumableCandidate" } } } @@ -18799,34 +18542,22 @@ "description": "" }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestError" - } - } - }, - "description": "" + "description": "Machine operation is not permitted." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestError" - } - } - }, - "description": "" + "description": "Machine not found." } } - }, - "post": { - "operationId": "api_v1_admin_makerspace_archive_requests_create", - "summary": "Request makerspace archival", + } + }, + "/api/v1/admin/machines/{id}/consumables": { + "get": { + "operationId": "api_v1_admin_machines_consumables_list", + "summary": "List machine consumables", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -18834,91 +18565,38 @@ } ], "tags": [ - "Admin makerspaces" + "Admin machines" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MakerspaceArchiveRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MakerspaceArchiveRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MakerspaceArchiveRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MakerspaceArchiveRequest" + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineConsumable" + } } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestValidationError" - } - } - }, - "description": "The reason was blank or too long." - }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestError" - } - } - }, - "description": "" + "description": "Machine operation is not permitted." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestError" - } - } - }, - "description": "" + "description": "Machine not found." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/archive-requests/{id}/withdraw": { + }, "post": { - "operationId": "api_v1_admin_makerspace_archive_requests_withdraw_create", - "summary": "Withdraw a pending makerspace archive request", + "operationId": "api_v1_admin_machines_consumables_create", + "summary": "Link a count or grams consumable to a machine", "parameters": [ { "in": "path", @@ -18927,92 +18605,83 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Admin makerspaces" + "Admin machines" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LinkMachineConsumable" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/LinkMachineConsumable" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/LinkMachineConsumable" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MakerspaceArchiveRequest" + "$ref": "#/components/schemas/MachineConsumable" } } }, "description": "" }, + "400": { + "description": "Invalid consumable." + }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestError" - } - } - }, - "description": "" + "description": "Machine management is not permitted." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ArchiveRequestError" - } - } - }, - "description": "" + "description": "Machine not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/audit-verifications": { - "get": { - "operationId": "api_v1_admin_makerspace_audit_verifications_retrieve", - "summary": "List audit verification runs for one makerspace", + "/api/v1/admin/machines/{id}/consumables/{cid}": { + "delete": { + "operationId": "api_v1_admin_machines_consumables_destroy", + "summary": "Unlink a machine consumable", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "cid", "schema": { "type": "integer" }, "required": true }, { - "in": "query", - "name": "page", + "in": "path", + "name": "id", "schema": { "type": "integer" }, - "description": "Page number (24 runs per page)." + "required": true } ], "tags": [ - "Audit verification" + "Admin machines" ], "security": [ { @@ -19020,66 +18689,37 @@ } ], "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VerificationRunPage" - } - } - }, - "description": "" + "204": { + "description": "No response body" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationBadRequest" - } - } - }, - "description": "Invalid filters, `narrow_audit_range` or `audit_verification_scope_too_large`." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } - } - }, - "description": "Not authenticated." + "description": "Consumable is not linked." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } - } - }, - "description": "Not active staff." + "description": "Machine management is not permitted." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } - } - }, - "description": "Makerspace or run not in the caller's VIEW_AUDIT scope." + "description": "Machine not found." } } - }, + } + }, + "/api/v1/admin/machines/{id}/consumables/{cid}/log": { "post": { - "operationId": "api_v1_admin_makerspace_audit_verifications_create", - "description": "Always asynchronous: 202 with the new or deduplicated run (`deduplicated`), then poll `status_url`. One active run per makerspace (409 `audit_verification_busy`); at most 10,000 selected rows and bounded expanded work (400); throttled per user (429).", - "summary": "Request asynchronous verification of the filtered audit rows", + "operationId": "api_v1_admin_machines_consumables_log_create", + "summary": "Log machine consumable usage", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "cid", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "id", "schema": { "type": "integer" }, @@ -19087,26 +18727,27 @@ } ], "tags": [ - "Audit verification" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VerificationRequest" + "$ref": "#/components/schemas/LogMachineConsumption" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/VerificationRequest" + "$ref": "#/components/schemas/LogMachineConsumption" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/VerificationRequest" + "$ref": "#/components/schemas/LogMachineConsumption" } } - } + }, + "required": true }, "security": [ { @@ -19114,95 +18755,36 @@ } ], "responses": { - "202": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VerificationAccepted" + "$ref": "#/components/schemas/MachineConsumable" } } }, "description": "" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationError" - } - } - }, - "description": "`audit_verification_busy`." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } - } - }, - "description": "Throttled." - }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationBadRequest" - } - } - }, - "description": "Invalid filters, `narrow_audit_range` or `audit_verification_scope_too_large`." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } - } - }, - "description": "Not authenticated." + "description": "Invalid quantity or insufficient stock." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } - } - }, - "description": "Not active staff." + "description": "Machine operation is not permitted." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } - } - }, - "description": "Makerspace or run not in the caller's VIEW_AUDIT scope." + "description": "Machine not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/audit-verifications/{run_id}": { + "/api/v1/admin/machines/{id}/documents": { "get": { - "operationId": "api_v1_admin_makerspace_audit_verifications_retrieve_2", - "summary": "Read one audit verification run's latest state and coverage", + "operationId": "api_v1_admin_machines_documents_list", + "summary": "List machine documents", "parameters": [ { "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "run_id", + "name": "id", "schema": { "type": "integer" }, @@ -19210,7 +18792,7 @@ } ], "tags": [ - "Audit verification" + "Admin machines" ], "security": [ { @@ -19222,63 +18804,86 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VerificationRun" + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineDocument" + } } } }, "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationBadRequest" - } - } + } + } + }, + "post": { + "operationId": "api_v1_admin_machines_documents_create", + "summary": "Finalize a machine document upload", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" }, - "description": "Invalid filters, `narrow_audit_range` or `audit_verification_scope_too_large`." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } + "required": true + } + ], + "tags": [ + "Admin machines" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentFinalize" } }, - "description": "Not authenticated." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DocumentFinalize" } }, - "description": "Not active staff." + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DocumentFinalize" + } + } }, - "404": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AuditVerificationDetailError" + "$ref": "#/components/schemas/MachineDocument" } } }, - "description": "Makerspace or run not in the caller's VIEW_AUDIT scope." + "description": "" + }, + "400": { + "description": "Invalid machine document." + }, + "503": { + "description": "Machine document storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/backup-custody": { - "get": { - "operationId": "api_v1_admin_makerspace_backup_custody_retrieve", - "summary": "Get the makerspace's archive and tenant-exit custody status", + "/api/v1/admin/machines/{id}/documents/presign": { + "post": { + "operationId": "api_v1_admin_machines_documents_presign_create", + "summary": "Create a machine document upload URL", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -19286,47 +18891,54 @@ } ], "tags": [ - "Backup recipients" + "Admin machines" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentPresign" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DocumentPresign" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DocumentPresign" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BackupCustody" - } - } - }, - "description": "" + "201": { + "description": "Machine document upload details." }, "400": { - "description": "The backup module is not installed for this makerspace." - }, - "404": { - "description": "The requested resource does not exist in the actor's scope." - }, - "401": { - "description": "Authentication is required." + "description": "Invalid document upload request." }, - "403": { - "description": "The authenticated actor is not authorized." + "503": { + "description": "Machine document storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/backups": { + "/api/v1/admin/machines/{id}/error-logs": { "get": { - "operationId": "api_v1_admin_makerspace_backups_list", - "summary": "List makerspace backup archives", + "operationId": "api_v1_admin_machines_error_logs_list", + "summary": "List machine error logs", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -19334,7 +18946,7 @@ } ], "tags": [ - "Backup" + "Admin machines" ], "security": [ { @@ -19348,34 +18960,22 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/BackupArchive" + "$ref": "#/components/schemas/MachineErrorLog" } } } }, "description": "" - }, - "400": { - "description": "The backup module is not installed for this makerspace." - }, - "404": { - "description": "The requested resource does not exist in the actor's scope." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "The authenticated actor is not authorized." } } }, "post": { - "operationId": "api_v1_admin_makerspace_backups_create", - "summary": "Request an age-encrypted makerspace backup", + "operationId": "api_v1_admin_machines_error_logs_create", + "summary": "Log a machine error", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -19383,51 +18983,58 @@ } ], "tags": [ - "Backup" + "Admin machines" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LogError" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/LogError" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/LogError" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "202": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BackupArchive" + "$ref": "#/components/schemas/MachineErrorLog" } } }, "description": "" }, "400": { - "description": "The backup module is not installed for this makerspace." - }, - "404": { - "description": "The requested resource does not exist in the actor's scope." - }, - "503": { - "description": "The backup worker is unavailable." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "The authenticated actor is not authorized." + "description": "Invalid error log." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/billing-settings": { - "get": { - "operationId": "api_v1_admin_makerspace_billing_settings_retrieve", - "description": "Requires manage_makerspace. No payments module or credentials required; billing.reminders is a separate opt-in.", - "summary": "Read billing, receipt and accounting policy", + "/api/v1/admin/machines/{id}/image": { + "post": { + "operationId": "api_v1_admin_machines_image_create", + "summary": "Create a machine image upload URL", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -19435,74 +19042,65 @@ } ], "tags": [ - "Payments" + "Admin machines" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BillingSettings" + "$ref": "#/components/schemas/PublicImageUploadResponse" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BillingPolicyError" - } - } - }, - "description": "Invalid billing policy fields." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Invalid image upload request." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Machine management access is required." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Machine not found." + }, + "503": { + "description": "Public image storage is unavailable." } } }, - "patch": { - "operationId": "api_v1_admin_makerspace_billing_settings_partial_update", - "description": "Requires manage_makerspace. Terms apply only to new charges. Reminders require billing.reminders, notifications and email; no online rail required.", - "summary": "Change billing, receipt and accounting policy", + "put": { + "operationId": "api_v1_admin_machines_image_update", + "summary": "Attach an uploaded image to a machine", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -19510,26 +19108,27 @@ } ], "tags": [ - "Payments" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedBillingSettings" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedBillingSettings" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedBillingSettings" + "$ref": "#/components/schemas/PublicImageAttachRequest" } } - } + }, + "required": true }, "security": [ { @@ -19541,80 +19140,128 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BillingSettings" + "$ref": "#/components/schemas/Machine" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BillingPolicyError" - } - } - }, - "description": "Invalid billing policy fields." + "description": "Invalid image object key or size." }, - "401": { + "403": { + "description": "Machine management access is required." + }, + "404": { + "description": "Machine not found." + }, + "503": { + "description": "Public image storage is unavailable." + } + } + }, + "delete": { + "operationId": "api_v1_admin_machines_image_destroy", + "summary": "Clear a machine image", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/Machine" } } }, "description": "" }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Machine management access is required." }, "404": { + "description": "Machine not found." + } + } + } + }, + "/api/v1/admin/machines/{id}/operator-candidates": { + "get": { + "operationId": "api_v1_admin_machines_operator_candidates_list", + "summary": "List active members eligible for machine operator assignment", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/OperatorCandidate" + } } } }, "description": "" + }, + "403": { + "description": "Operator delegation is not permitted." + }, + "404": { + "description": "Machine not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/categories": { + "/api/v1/admin/machines/{id}/operators": { "get": { - "operationId": "api_v1_admin_makerspace_categories_list", - "summary": "List or create inventory categories", + "operationId": "api_v1_admin_machines_operators_list", + "summary": "List machine operators", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "Admin inventory" + "Admin machines" ], "security": [ { @@ -19626,7 +19273,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedCategoryAdminList" + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineOperator" + } } } }, @@ -19635,12 +19285,12 @@ } }, "post": { - "operationId": "api_v1_admin_makerspace_categories_create", - "summary": "List or create inventory categories", + "operationId": "api_v1_admin_machines_operators_create", + "summary": "Assign a machine operator", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -19648,23 +19298,23 @@ } ], "tags": [ - "Admin inventory" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CategoryAdmin" + "$ref": "#/components/schemas/AssignOperator" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CategoryAdmin" + "$ref": "#/components/schemas/AssignOperator" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CategoryAdmin" + "$ref": "#/components/schemas/AssignOperator" } } }, @@ -19680,40 +19330,62 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CategoryAdmin" + "$ref": "#/components/schemas/MachineOperator" } } }, "description": "" + }, + "400": { + "description": "Invalid operator assignment." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/certification-types": { - "get": { - "operationId": "api_v1_admin_makerspace_certification_types_retrieve", - "summary": "List certification types in a makerspace", + "/api/v1/admin/machines/{id}/operators/{user_pk}": { + "patch": { + "operationId": "api_v1_admin_machines_operators_partial_update", + "summary": "Update a machine operator", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true }, { - "in": "query", - "name": "page", + "in": "path", + "name": "user_pk", "schema": { "type": "integer" }, - "description": "Page number (24 rows per page)." + "required": true } ], "tags": [ "Admin machines" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedAssignOperator" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedAssignOperator" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedAssignOperator" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -19724,33 +19396,95 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationTypePage" + "$ref": "#/components/schemas/MachineOperator" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Invalid operator assignment." + } + } + }, + "delete": { + "operationId": "api_v1_admin_machines_operators_destroy", + "summary": "Remove a machine operator", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + { + "in": "path", + "name": "user_pk", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } + } + }, + "/api/v1/admin/machines/{id}/publicity": { + "get": { + "operationId": "api_v1_admin_machines_publicity_retrieve", + "summary": "Preview a machine public listing", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicMachine" + } + } + }, + "description": "" } } }, - "post": { - "operationId": "api_v1_admin_makerspace_certification_types_create", - "summary": "Create a certification type", + "patch": { + "operationId": "api_v1_admin_machines_publicity_partial_update", + "summary": "Set machine public visibility", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -19764,21 +19498,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationType" + "$ref": "#/components/schemas/PatchedMachinePublicity" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CertificationType" + "$ref": "#/components/schemas/PatchedMachinePublicity" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CertificationType" + "$ref": "#/components/schemas/PatchedMachinePublicity" } } - }, - "required": true + } }, "security": [ { @@ -19786,68 +19519,44 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificationType" + "$ref": "#/components/schemas/PublicMachine" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." + "description": "Invalid publicity setting." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "MANAGE_MACHINES is required." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace frozen for tenant migration." + "description": "Machine not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/containers": { - "get": { - "operationId": "api_v1_admin_makerspace_containers_list", - "summary": "List containers", + "/api/v1/admin/machines/{id}/retire": { + "post": { + "operationId": "api_v1_admin_machines_retire_create", + "summary": "Retire a machine", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } - }, - { - "name": "page_size", - "required": false, - "in": "query", - "description": "Number of results to return per page.", - "schema": { - "type": "integer" - } } ], "tags": [ - "Containers" + "Admin machines" ], "security": [ { @@ -19859,61 +19568,23 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedBoxList" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/Machine" } } }, "description": "" } } - }, + } + }, + "/api/v1/admin/machines/{id}/set-status": { "post": { - "operationId": "api_v1_admin_makerspace_containers_create", - "summary": "Create container", + "operationId": "api_v1_admin_machines_set_status_create", + "summary": "Set a machine status", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -19921,23 +19592,23 @@ } ], "tags": [ - "Containers" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/SetStatus" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/SetStatus" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/SetStatus" } } }, @@ -19949,51 +19620,50 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/Machine" } } }, "description": "" }, "400": { + "description": "Invalid machine status." + } + } + } + }, + "/api/v1/admin/machines/{id}/unretire": { + "post": { + "operationId": "api_v1_admin_machines_unretire_create", + "summary": "Reactivate a retired machine", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/Machine" } } }, @@ -20002,14 +19672,14 @@ } } }, - "/api/v1/admin/makerspace/{makerspace_id}/cover": { - "post": { - "operationId": "api_v1_admin_makerspace_cover_create", - "summary": "Create a makerspace public image upload URL", + "/api/v1/admin/machines/{id}/usage": { + "get": { + "operationId": "api_v1_admin_machines_usage_list", + "summary": "List machine usage entries", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -20017,65 +19687,36 @@ } ], "tags": [ - "Admin makerspaces" + "Admin machines" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineUsageEntry" + } } } }, "description": "" - }, - "400": { - "description": "Invalid image upload request." - }, - "403": { - "description": "The actor lacks manage_makerspace here." - }, - "404": { - "description": "Makerspace not found, archived, not active, or outside the actor's scope." - }, - "503": { - "description": "Public image storage is unavailable." } } }, - "put": { - "operationId": "api_v1_admin_makerspace_cover_update", - "summary": "Attach an uploaded public image to a makerspace", + "post": { + "operationId": "api_v1_admin_machines_usage_create", + "summary": "Log machine usage", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -20083,23 +19724,23 @@ } ], "tags": [ - "Admin makerspaces" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "$ref": "#/components/schemas/LogUsage" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "$ref": "#/components/schemas/LogUsage" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" + "$ref": "#/components/schemas/LogUsage" } } }, @@ -20111,45 +19752,52 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Makerspace" + "$ref": "#/components/schemas/MachineUsageEntry" } } }, "description": "" }, "400": { - "description": "Invalid image object key or size." - }, - "403": { - "description": "The actor lacks manage_makerspace here." - }, - "404": { - "description": "Makerspace not found, archived, not active, or outside the actor's scope." - }, - "503": { - "description": "Public image storage is unavailable." + "description": "Invalid usage entry." } } - }, - "delete": { - "operationId": "api_v1_admin_makerspace_cover_destroy", - "summary": "Clear a makerspace public image", + } + }, + "/api/v1/admin/machines/{id}/usage-candidates": { + "get": { + "operationId": "api_v1_admin_machines_usage_candidates_retrieve", + "summary": "Find active members for manual usage attribution", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + } } ], "tags": [ - "Admin makerspaces" + "Admin machines" ], "security": [ { @@ -20161,29 +19809,35 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Makerspace" + "$ref": "#/components/schemas/MachineUsageCandidatesPage" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, "403": { - "description": "The actor lacks manage_makerspace here." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "description": "Makerspace not found, archived, not active, or outside the actor's scope." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/dashboard": { + "/api/v1/admin/machines/{id}/warranty": { "get": { - "operationId": "api_v1_admin_makerspace_dashboard_retrieve", - "summary": "Staff operations dashboard counts", + "operationId": "api_v1_admin_machines_warranty_retrieve", + "summary": "Retrieve warranty details for a machine", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -20191,7 +19845,7 @@ } ], "tags": [ - "Dashboard" + "Admin warranty" ], "security": [ { @@ -20203,7 +19857,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Dashboard" + "$ref": "#/components/schemas/Warranty" } } }, @@ -20217,7 +19871,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -20227,7 +19881,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -20237,59 +19891,17 @@ } } }, - "description": "Makerspace not found." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/data-exports": { - "get": { - "operationId": "api_v1_admin_makerspace_data_exports_list", - "summary": "List makerspace data-export jobs", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Data exports" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DataExportJob" - } - } - } - }, "description": "" - }, - "403": { - "description": "MANAGE_MAKERSPACE is required." } } }, - "post": { - "operationId": "api_v1_admin_makerspace_data_exports_create", - "summary": "Request a redacted makerspace data export", + "put": { + "operationId": "api_v1_admin_machines_warranty_update", + "summary": "Create or update warranty details for a machine", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -20297,23 +19909,23 @@ } ], "tags": [ - "Data exports" + "Admin warranty" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DataExportCreate" + "$ref": "#/components/schemas/WarrantyUpsert" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/DataExportCreate" + "$ref": "#/components/schemas/WarrantyUpsert" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/DataExportCreate" + "$ref": "#/components/schemas/WarrantyUpsert" } } } @@ -20324,45 +19936,89 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DataExportJob" + "$ref": "#/components/schemas/Warranty" } } }, "description": "" }, "400": { - "description": "Invalid fidelity or quota/module policy failure." + "description": "Invalid warranty details." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "MANAGE_MAKERSPACE is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "429": { - "description": "Export creation rate limit exceeded." + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/data-exports/{job_id}": { - "get": { - "operationId": "api_v1_admin_makerspace_data_exports_retrieve", - "summary": "Poll a makerspace data-export job", + "/api/v1/admin/machines/documents/{id}": { + "delete": { + "operationId": "api_v1_admin_machines_documents_destroy", + "summary": "Delete a machine document", "parameters": [ { "in": "path", - "name": "job_id", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true - }, + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + } + } + } + }, + "/api/v1/admin/machines/documents/{id}/url": { + "get": { + "operationId": "api_v1_admin_machines_documents_url_retrieve", + "summary": "Create a signed machine document view URL", + "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -20370,7 +20026,7 @@ } ], "tags": [ - "Data exports" + "Admin machines" ], "security": [ { @@ -20379,41 +20035,22 @@ ], "responses": { "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DataExportJob" - } - } - }, - "description": "" - }, - "403": { - "description": "MANAGE_MAKERSPACE is required." + "description": "Signed machine document URL." }, - "404": { - "description": "Export job not found in this makerspace." + "503": { + "description": "Machine document storage is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/data-exports/{job_id}/download-url": { - "post": { - "operationId": "api_v1_admin_makerspace_data_exports_download_url_create", - "summary": "Issue a short-lived one-use export download URL", + "/api/v1/admin/maintenance/log-documents/{id}/": { + "delete": { + "operationId": "api_v1_admin_maintenance_log_documents_destroy", + "summary": "Delete a maintenance document", "parameters": [ { "in": "path", - "name": "job_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -20421,7 +20058,7 @@ } ], "tags": [ - "Data exports" + "Admin maintenance" ], "security": [ { @@ -20429,52 +20066,78 @@ } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DataExportDownloadUrl" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - }, - "400": { - "description": "Export is not available." + "description": "Invalid request." }, "403": { - "description": "MANAGE_MAKERSPACE is required; PORTABLE additionally requires source superadmin." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Export job not found in this makerspace." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/device-printer-bindings": { + "/api/v1/admin/maintenance/log-documents/{id}/url/": { "get": { - "operationId": "api_v1_admin_makerspace_device_printer_bindings_list", + "operationId": "api_v1_admin_maintenance_log_documents_url_retrieve", + "summary": "Create a private maintenance document URL", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "Devices" + "Admin maintenance" ], "security": [ { @@ -20486,7 +20149,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedBindingList" + "$ref": "#/components/schemas/MaintenanceDocumentUrl" } } }, @@ -20496,80 +20159,63 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Invalid payload or adapter module disabled." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication/signature/replay refused." + "description": "Invalid request." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authority or device revoked." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Target outside scope or absent." + "description": "Not found." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Conflicting command/report." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Delivery lease expired." + "description": "Workflow conflict." }, - "423": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Service unavailable." } } - }, + } + }, + "/api/v1/admin/maintenance/logs/{id}/documents/": { "post": { - "operationId": "api_v1_admin_makerspace_device_printer_bindings_create", + "operationId": "api_v1_admin_maintenance_logs_documents_create", + "summary": "Finalize a maintenance document upload", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -20577,23 +20223,23 @@ } ], "tags": [ - "Devices" + "Admin maintenance" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BindingCreate" + "$ref": "#/components/schemas/MaintenanceDocumentFinalize" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/BindingCreate" + "$ref": "#/components/schemas/MaintenanceDocumentFinalize" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/BindingCreate" + "$ref": "#/components/schemas/MaintenanceDocumentFinalize" } } }, @@ -20609,7 +20255,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Binding" + "$ref": "#/components/schemas/MaintenanceLogDocument" } } }, @@ -20619,101 +20265,92 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Invalid payload or adapter module disabled." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication/signature/replay refused." + "description": "Invalid request." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authority or device revoked." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Target outside scope or absent." + "description": "Not found." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Conflicting command/report." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Delivery lease expired." + "description": "Workflow conflict." }, - "423": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Service unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/devices": { - "get": { - "operationId": "api_v1_admin_makerspace_devices_list", - "summary": "List a makerspace's devices", + "/api/v1/admin/maintenance/logs/{id}/documents/presign/": { + "post": { + "operationId": "api_v1_admin_maintenance_logs_documents_presign_create", + "summary": "Create a maintenance document upload URL", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "Devices" + "Admin maintenance" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaintenanceDocumentPresign" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MaintenanceDocumentPresign" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MaintenanceDocumentPresign" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -20724,7 +20361,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedDeviceList" + "$ref": "#/components/schemas/MaintenanceDocumentPresignResponse" } } }, @@ -20734,61 +20371,63 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request or module disabled." + "description": "Invalid request." }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "Permission denied." }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "Not found." }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found." + "description": "Workflow conflict." }, - "423": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Service unavailable." } } - }, - "post": { - "operationId": "api_v1_admin_makerspace_devices_create", - "summary": "Create a pending device and its first enrollment", + } + }, + "/api/v1/admin/maintenance/schedules/{id}/": { + "patch": { + "operationId": "api_v1_admin_maintenance_schedules_partial_update", + "summary": "Update a maintenance schedule", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -20796,27 +20435,26 @@ } ], "tags": [ - "Devices" + "Admin maintenance" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceCreate" + "$ref": "#/components/schemas/PatchedMaintenanceScheduleWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/DeviceCreate" + "$ref": "#/components/schemas/PatchedMaintenanceScheduleWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/DeviceCreate" + "$ref": "#/components/schemas/PatchedMaintenanceScheduleWrite" } } - }, - "required": true + } }, "security": [ { @@ -20824,93 +20462,53 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceCreated" + "$ref": "#/components/schemas/MaintenanceSchedule" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Invalid request or module disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." }, "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Tenant frozen for migration." + "description": "Makerspace writes are frozen for tenant migration." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/direct-loan-members": { - "get": { - "operationId": "api_v1_admin_makerspace_direct_loan_members_list", + "/api/v1/admin/maintenance/schedules/{id}/deactivate/": { + "post": { + "operationId": "api_v1_admin_maintenance_schedules_deactivate_create", + "summary": "Deactivate a maintenance schedule", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "api" + "Admin maintenance" ], "security": [ { @@ -20922,20 +20520,49 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedDirectLoanMemberList" + "$ref": "#/components/schemas/MaintenanceSchedule" } } }, "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/direct-loans": { + "/api/v1/admin/makerspace/{makerspace_id}/accepted-requests": { "get": { - "operationId": "api_v1_admin_makerspace_direct_loans_list", - "summary": "List direct handout loans", + "operationId": "api_v1_admin_makerspace_accepted_requests_list", + "summary": "List accepted requests awaiting issue", "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "description": "Select one result within the authorized list.", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 9223372036854775807 + } + }, { "in": "path", "name": "makerspace_id", @@ -20944,6 +20571,15 @@ }, "required": true }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" + } + }, { "name": "page", "required": false, @@ -20952,6 +20588,15 @@ "schema": { "type": "integer" } + }, + { + "name": "search", + "required": false, + "in": "query", + "description": "A search term (requested-for, requester name/email).", + "schema": { + "type": "string" + } } ], "tags": [ @@ -20967,7 +20612,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedDirectLoanList" + "$ref": "#/components/schemas/PaginatedAdminRequestList" } } }, @@ -21012,22 +20657,14 @@ } }, "description": "Not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Workflow conflict." } } - }, - "post": { - "operationId": "api_v1_admin_makerspace_direct_loans_create", - "summary": "Issue direct handout without public request", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/accountability": { + "get": { + "operationId": "api_v1_admin_makerspace_accountability_retrieve", + "summary": "Requester accountability dashboard", "parameters": [ { "in": "path", @@ -21039,39 +20676,20 @@ } ], "tags": [ - "Admin requests" + "Analytics" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DirectLoanIssue" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DirectLoanIssue" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DirectLoanIssue" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DirectLoan" + "type": "object", + "additionalProperties": {} } } }, @@ -21081,17 +20699,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Invalid request." + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, @@ -21101,7 +20719,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, @@ -21111,40 +20729,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Workflow conflict." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Service unavailable." + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/email-logs": { + "/api/v1/admin/makerspace/{makerspace_id}/active-loans": { "get": { - "operationId": "api_v1_admin_makerspace_email_logs_list", - "summary": "List makerspace email delivery logs", + "operationId": "api_v1_admin_makerspace_active_loans_list", + "summary": "List active loans awaiting return", "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "description": "Select one result within the authorized list.", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 9223372036854775807 + } + }, { "in": "path", "name": "makerspace_id", @@ -21154,32 +20763,35 @@ "required": true }, { - "name": "page", + "name": "ordering", "required": false, "in": "query", - "description": "A page number within the paginated result set.", + "description": "Which field to use when ordering the results.", "schema": { - "type": "integer" + "type": "string" } }, { + "name": "page", + "required": false, "in": "query", - "name": "recent_days", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "description": "Limit to the last 1–365 days." + } }, { + "name": "search", + "required": false, "in": "query", - "name": "status", + "description": "A search term (requested-for, requester name/email).", "schema": { "type": "string" } } ], "tags": [ - "Email logs" + "Admin requests" ], "security": [ { @@ -21191,51 +20803,169 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedEmailLogList" + "$ref": "#/components/schemas/PaginatedAdminRequestList" } } }, "description": "" }, "400": { - "description": "Invalid status filter." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." }, "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, "description": "Authentication required." }, "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, "description": "Permission denied." }, "404": { - "description": "Makerspace not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/email-logs/{id}/retry": { - "post": { - "operationId": "api_v1_admin_makerspace_email_logs_retry_create", - "summary": "Retry a failed makerspace email", + "/api/v1/admin/makerspace/{makerspace_id}/analytics/{report_key}": { + "get": { + "operationId": "api_v1_admin_makerspace_analytics_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "months", "schema": { "type": "integer" }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "path", + "name": "report_key", + "schema": { + "type": "string" + }, "required": true + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Email logs" + "Analytics" ], "security": [ { @@ -21247,196 +20977,162 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailLog" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" }, "400": { - "description": "Email log cannot be retried." - }, - "404": { - "description": "Email log not found." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/email-templates": { - "get": { - "operationId": "api_v1_admin_makerspace_email_templates_list", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Email templates" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EmailTemplateListItem" - } + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/email-templates/{stream}/{audience}/{key}": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/active-loans": { "get": { - "operationId": "api_v1_admin_makerspace_email_templates_retrieve", + "operationId": "api_v1_admin_makerspace_analytics_active_loans_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { - "in": "path", - "name": "audience", + "in": "query", + "name": "end", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "format": "date" + } }, { - "in": "path", - "name": "key", + "in": "query", + "name": "grain", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "limit", "schema": { "type": "integer" - }, - "required": true + } }, { "in": "path", - "name": "stream", + "name": "makerspace_id", "schema": { - "type": "string" + "type": "integer" }, "required": true - } - ], - "tags": [ - "Email templates" - ], - "security": [ + }, { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EmailTemplateDetail" - } - } + "in": "query", + "name": "months", + "schema": { + "type": "integer" }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - }, - "patch": { - "operationId": "api_v1_admin_makerspace_email_templates_partial_update", - "parameters": [ { - "in": "path", - "name": "audience", + "in": "query", + "name": "start", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "format": "date" + } }, { - "in": "path", - "name": "key", + "in": "query", + "name": "status", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "subject_id", "schema": { "type": "integer" - }, - "required": true + } }, { - "in": "path", - "name": "stream", + "in": "query", + "name": "subject_kind", "schema": { "type": "string" }, - "required": true + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Email templates" + "Analytics" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -21447,46 +21143,87 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailTemplateDetail" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid report request." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/email-templates/{stream}/{audience}/{key}/reset": { - "post": { - "operationId": "api_v1_admin_makerspace_email_templates_reset_create", + "/api/v1/admin/makerspace/{makerspace_id}/analytics/booking-utilization": { + "get": { + "operationId": "api_v1_admin_makerspace_analytics_booking_utilization_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { - "in": "path", - "name": "audience", + "in": "query", + "name": "end", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "format": "date" + } }, { - "in": "path", - "name": "key", + "in": "query", + "name": "grain", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } }, { "in": "path", @@ -21497,16 +21234,70 @@ "required": true }, { - "in": "path", - "name": "stream", + "in": "query", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", "schema": { "type": "string" }, - "required": true + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Email templates" + "Analytics" ], "security": [ { @@ -21518,54 +21309,87 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailTemplateDetail" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid report request." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/email-templates/{stream}/{audience}/{key}/types/{machine_type_id}": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/damaged-lost": { "get": { - "operationId": "api_v1_admin_makerspace_email_templates_types_retrieve", + "operationId": "api_v1_admin_makerspace_analytics_damaged_lost_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { - "in": "path", - "name": "audience", + "in": "query", + "name": "end", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "format": "date" + } }, { - "in": "path", - "name": "key", + "in": "query", + "name": "grain", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } }, { - "in": "path", - "name": "machine_type_id", + "in": "query", + "name": "limit", "schema": { "type": "integer" - }, - "required": true + } }, { "in": "path", @@ -21576,16 +21400,70 @@ "required": true }, { - "in": "path", - "name": "stream", + "in": "query", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", "schema": { "type": "string" }, - "required": true + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Email templates" + "Analytics" ], "security": [ { @@ -21597,52 +21475,87 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailTemplateDetail" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid report request." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } - }, - "patch": { - "operationId": "api_v1_admin_makerspace_email_templates_types_partial_update", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/analytics/damaged-missing": { + "get": { + "operationId": "api_v1_admin_makerspace_analytics_damaged_missing_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { - "in": "path", - "name": "audience", + "in": "query", + "name": "end", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "format": "date" + } }, { - "in": "path", - "name": "key", + "in": "query", + "name": "grain", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } }, { - "in": "path", - "name": "machine_type_id", + "in": "query", + "name": "limit", "schema": { "type": "integer" - }, - "required": true + } }, { "in": "path", @@ -21653,114 +21566,70 @@ "required": true }, { - "in": "path", - "name": "stream", + "in": "query", + "name": "months", "schema": { - "type": "string" - }, - "required": true - } - ], - "tags": [ - "Email templates" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EmailTemplateDetail" - } - } + "type": "integer" }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/email-templates/{stream}/{audience}/{key}/types/{machine_type_id}/reset": { - "post": { - "operationId": "api_v1_admin_makerspace_email_templates_types_reset_create", - "parameters": [ { - "in": "path", - "name": "audience", + "in": "query", + "name": "start", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "format": "date" + } }, { - "in": "path", - "name": "key", + "in": "query", + "name": "status", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } }, { - "in": "path", - "name": "machine_type_id", + "in": "query", + "name": "subject_id", "schema": { "type": "integer" - }, - "required": true + } }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "subject_kind", "schema": { - "type": "integer" + "type": "string" }, - "required": true + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." }, { - "in": "path", - "name": "stream", + "in": "query", + "name": "subject_type", "schema": { - "type": "string" - }, - "required": true + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Email templates" + "Analytics" ], "security": [ { @@ -21772,170 +21641,87 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailTemplateDetail" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/email-templates/preview": { - "post": { - "operationId": "api_v1_admin_makerspace_email_templates_preview_create", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Email templates" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EmailTemplatePreviewRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/EmailTemplatePreviewRequest" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/EmailTemplatePreviewRequest" - } - } + "description": "Invalid report request." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailTemplatePreviewResponse" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/integration-health": { - "get": { - "operationId": "api_v1_admin_makerspace_integration_health_retrieve", - "summary": "Get makerspace integration health", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Integration health" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/IntegrationHealth" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Not allowed to manage this makerspace." + "description": "Permission denied." }, "404": { - "description": "Makerspace not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/inventory": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/event-attendance": { "get": { - "operationId": "api_v1_admin_makerspace_inventory_list", - "summary": "List or create inventory products", + "operationId": "api_v1_admin_makerspace_analytics_event_attendance_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { "in": "query", - "name": "archived", + "name": "end", "schema": { - "type": "string" - }, - "description": "`true` for archived, `false` for active; `all` or omitted includes both." + "type": "string", + "format": "date" + } }, { - "name": "id", "in": "query", - "required": false, - "description": "Select one result within the authorized list.", + "name": "grain", "schema": { - "type": "integer", - "minimum": 1, - "maximum": 9223372036854775807 + "type": "string", + "enum": [ + "day", + "month", + "week" + ] } }, { "in": "query", - "name": "low_stock", + "name": "limit", "schema": { - "type": "string" - }, - "description": "`true` to list items at or below 20% of total." + "type": "integer" + } }, { "in": "path", @@ -21947,52 +21733,69 @@ }, { "in": "query", - "name": "ordering", + "name": "months", "schema": { - "type": "string" + "type": "integer" }, - "description": "Allowlisted sort field; prefix with '-' for descending." + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." }, { "in": "query", - "name": "out_of_stock", + "name": "start", "schema": { "type": "string", - "enum": [ - "false", - "true" - ] - }, - "description": "`true` for exactly zero available, matching the dashboard tile." + "format": "date" + } }, { "in": "query", - "name": "page", + "name": "status", "schema": { - "type": "integer" - }, - "description": "Page number, 24 rows per page." + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } }, { - "name": "page_size", - "required": false, "in": "query", - "description": "Number of results to return per page.", + "name": "subject_id", "schema": { "type": "integer" } }, { "in": "query", - "name": "q", + "name": "subject_kind", "schema": { "type": "string" }, - "description": "Full-text search over name, storage location, tracking mode and description (\"phrases\", -exclusions, typo-tolerant on the name), or a category name." + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Admin inventory" + "Analytics" ], "security": [ { @@ -22004,7 +21807,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedInventoryProductAdminList" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -22014,48 +21817,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or report not found." } } - }, - "post": { - "operationId": "api_v1_admin_makerspace_inventory_create", - "summary": "List or create inventory products", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/analytics/fablab-health": { + "get": { + "operationId": "api_v1_admin_makerspace_analytics_fablab_health_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, { "in": "path", "name": "makerspace_id", @@ -22063,42 +21896,84 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Admin inventory" + "Analytics" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InventoryProductAdminCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/InventoryProductAdminCreate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/InventoryProductAdminCreate" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InventoryProductAdmin" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -22108,85 +21983,76 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/inventory/export": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/machine-usage": { "get": { - "operationId": "api_v1_admin_makerspace_inventory_export_retrieve", - "summary": "Export inventory products as CSV or XLSX", + "operationId": "api_v1_admin_makerspace_analytics_machine_usage_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { "in": "query", - "name": "archived", + "name": "end", "schema": { "type": "string", - "enum": [ - "all", - "false", - "true" - ] + "format": "date" } }, { "in": "query", - "name": "format", + "name": "grain", "schema": { "type": "string", "enum": [ - "csv", - "xlsx" + "day", + "month", + "week" ] } }, { "in": "query", - "name": "ids", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "low_stock", + "name": "limit", "schema": { - "type": "boolean" + "type": "integer" } }, { @@ -22199,335 +22065,70 @@ }, { "in": "query", - "name": "out_of_stock", + "name": "months", "schema": { - "type": "string", - "enum": [ - "false", - "true" - ] + "type": "integer" }, - "description": "Exactly zero available, matching the dashboard list." + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." }, { "in": "query", - "name": "q", + "name": "start", "schema": { - "type": "string" + "type": "string", + "format": "date" } - } - ], - "tags": [ - "Admin inventory" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "text/csv": { - "schema": { - "type": "string" - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { - "schema": { - "type": "string", - "format": "binary" - } - } - }, - "description": "" - }, - "400": { - "description": "Invalid request." }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/inventory/import/apply": { - "post": { - "operationId": "api_v1_admin_makerspace_inventory_import_apply_create", - "summary": "Apply inventory bulk import", - "parameters": [ { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "status", "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Bulk import" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BulkImportPreview" - }, - "examples": { - "PreviewInventoryRows": { - "value": { - "rows": [ - { - "name": "Soldering Iron", - "total_quantity": 10, - "available_quantity": 8, - "is_public": true - } - ], - "mapping": { - "name": "name", - "total_quantity": "total_quantity" - } - }, - "summary": "Preview inventory rows" - } - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/BulkImportPreview" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/BulkImportPreview" - } + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "description": "Import application result." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/inventory/import/jobs": { - "post": { - "operationId": "api_v1_admin_makerspace_inventory_import_jobs_create", - "summary": "Create an async inventory bulk import job", - "parameters": [ + }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "subject_id", "schema": { "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Bulk import" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BulkImportJobCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/BulkImportJobCreate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/BulkImportJobCreate" - } } }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BulkImportJob" - } - } - }, - "description": "" - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/inventory/import/jobs/{job_id}": { - "get": { - "operationId": "api_v1_admin_makerspace_inventory_import_jobs_retrieve", - "summary": "Get async inventory bulk import job status", - "parameters": [ { - "in": "path", - "name": "job_id", + "in": "query", + "name": "subject_kind", "schema": { - "type": "integer" + "type": "string" }, - "required": true + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." }, { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Bulk import" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BulkImportJob" - } - } - }, - "description": "" - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/inventory/import/preview": { - "post": { - "operationId": "api_v1_admin_makerspace_inventory_import_preview_create", - "summary": "Preview inventory bulk import", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "subject_type", "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Bulk import" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BulkImportPreview" - }, - "examples": { - "PreviewInventoryRows": { - "value": { - "rows": [ - { - "name": "Soldering Iron", - "total_quantity": 10, - "available_quantity": 8, - "is_public": true - } - ], - "mapping": { - "name": "name", - "total_quantity": "total_quantity" - } - }, - "summary": "Preview inventory rows" - } - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/BulkImportPreview" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/BulkImportPreview" - } + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] } } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "description": "Import preview with row errors." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/inventory/reorder-draft": { - "post": { - "operationId": "api_v1_admin_makerspace_inventory_reorder_draft_create", - "summary": "Create or reuse a procurement reorder draft", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } ], "tags": [ - "Admin inventory" + "Analytics" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReorderDraft" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ReorderDraft" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ReorderDraft" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -22538,17 +22139,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyItem" - } - } - }, - "description": "" - }, - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ToBuyItem" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -22558,50 +22149,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/inventory/reorder-suggestions": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/maintenance-activity": { "get": { - "operationId": "api_v1_admin_makerspace_inventory_reorder_suggestions_list", - "summary": "List products at their reorder points", + "operationId": "api_v1_admin_makerspace_analytics_maintenance_activity_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, { "in": "path", "name": "makerspace_id", @@ -22611,17 +22230,70 @@ "required": true }, { - "name": "page", - "required": false, "in": "query", - "description": "A page number within the paginated result set.", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", "schema": { "type": "integer" } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Admin inventory" + "Analytics" ], "security": [ { @@ -22633,7 +22305,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedReorderSuggestionList" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -22643,93 +22315,151 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/kiosk-replays/": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/member-activity": { "get": { - "operationId": "api_v1_admin_makerspace_kiosk_replays_list", - "description": "Historical review remains available with inventory.kiosk disabled. No channel credentials or command payload are returned.", - "summary": "List failed handovers visible under current per-operation actions", + "operationId": "api_v1_admin_makerspace_analytics_member_activity_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "in": "path", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "name": "page", - "required": false, "in": "query", - "description": "A page number within the paginated result set.", + "name": "months", "schema": { "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" } }, { "in": "query", - "name": "resolution", + "name": "status", "schema": { "type": "string", "enum": [ - "all", - "resolved", - "unresolved" + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" ] } }, { "in": "query", - "name": "state", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", "schema": { "type": "string", "enum": [ - "failed" + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" ] } } ], "tags": [ - "Admin kiosk recovery" + "Analytics" ], "security": [ { @@ -22741,7 +22471,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedReplayRecoveryList" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -22751,17 +22481,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Invalid filter/resolution or disabled required module." + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, @@ -22771,50 +22501,58 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Account, current action or origin refused." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Failure is unavailable in your scope." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Resolution/replacement conflict." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Tenant source gate closed." + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/kiosk-stations/": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/most-lent": { "get": { - "operationId": "api_v1_admin_makerspace_kiosk_stations_list", - "summary": "List kiosk Stations", + "operationId": "api_v1_admin_makerspace_analytics_most_lent_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, { "in": "path", "name": "makerspace_id", @@ -22822,139 +22560,84 @@ "type": "integer" }, "required": true - } - ], - "tags": [ - "Admin kiosk stations" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KioskStation" - } - } - } - }, - "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" }, - "description": "Feature disabled or invalid request." + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Makerspace access or step-up denied." + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Station not found." + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rotate the PIN instead of revealing." + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" }, - "description": "Request rate limit exceeded." + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Credential secrets are unavailable." - } - } - }, - "post": { - "operationId": "api_v1_admin_makerspace_kiosk_stations_create", - "summary": "Create a kiosk Station and its first PIN", - "parameters": [ { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "subject_type", "schema": { - "type": "integer" - }, - "required": true + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Admin kiosk stations" + "Analytics" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/KioskStationCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/KioskStationCreate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/KioskStationCreate" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskStationSecret" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -22964,70 +22647,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Feature disabled or invalid request." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Makerspace access or step-up denied." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Station not found." + "description": "Invalid report request." }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Rotate the PIN instead of revealing." + "description": "Authentication required." }, - "429": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Request rate limit exceeded." + "description": "Permission denied." }, - "503": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Credential secrets are unavailable." + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/kit-checklist-preview": { - "post": { - "operationId": "api_v1_admin_makerspace_kit_checklist_preview_create", - "summary": "Preview required issue or return kit components", + "/api/v1/admin/makerspace/{makerspace_id}/analytics/payment-reconciliation": { + "get": { + "operationId": "api_v1_admin_makerspace_analytics_payment_reconciliation_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, { "in": "path", "name": "makerspace_id", @@ -23035,30 +22726,73 @@ "type": "integer" }, "required": true - } - ], - "tags": [ - "Admin requests" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/KitPreviewInput" - } + }, + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/KitPreviewInput" - } + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/KitPreviewInput" - } + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] } } - }, + ], + "tags": [ + "Analytics" + ], "security": [ { "jwtAuth": [] @@ -23069,7 +22803,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KitPreview" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -23079,134 +22813,151 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/ledger": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/qr-scans": { "get": { - "operationId": "api_v1_admin_makerspace_ledger_retrieve", - "summary": "List outstanding inventory loans", + "operationId": "api_v1_admin_makerspace_analytics_qr_scans_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "query", - "name": "overdue", + "name": "end", "schema": { - "type": "boolean" + "type": "string", + "format": "date" } }, { "in": "query", - "name": "page", + "name": "grain", "schema": { - "type": "integer" + "type": "string", + "enum": [ + "day", + "month", + "week" + ] } }, { "in": "query", - "name": "page_size", + "name": "limit", "schema": { "type": "integer" } }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "query", - "name": "search", + "name": "months", "schema": { - "type": "string" + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" } }, { "in": "query", - "name": "sort", + "name": "status", "schema": { "type": "string", "enum": [ - "-due", - "-holder", - "-item_name", - "-makerspace_id", - "-quantity", - "-since", - "-source", - "due", - "holder", - "item_name", - "makerspace_id", - "quantity", - "since", - "source" + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" ] } }, { "in": "query", - "name": "source", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", "schema": { "type": "string", "enum": [ - "direct", - "reviewed", - "self_checkout" + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" ] } } ], "tags": [ - "Ledger" + "Analytics" ], "security": [ { @@ -23218,31 +22969,88 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LedgerResponse" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid report request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/ledger/export": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/recently-added": { "get": { - "operationId": "api_v1_admin_makerspace_ledger_export_retrieve", - "summary": "Export outstanding inventory loans", + "operationId": "api_v1_admin_makerspace_analytics_recently_added_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { "in": "query", - "name": "format", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", "schema": { "type": "string", "enum": [ - "csv", - "xlsx" + "day", + "month", + "week" ] } }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, { "in": "path", "name": "makerspace_id", @@ -23253,121 +23061,70 @@ }, { "in": "query", - "name": "overdue", + "name": "months", "schema": { - "type": "boolean" - } + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." }, { "in": "query", - "name": "search", + "name": "start", "schema": { - "type": "string" + "type": "string", + "format": "date" } }, { "in": "query", - "name": "sort", + "name": "status", "schema": { "type": "string", "enum": [ - "-due", - "-holder", - "-item_name", - "-makerspace_id", - "-quantity", - "-since", - "-source", - "due", - "holder", - "item_name", - "makerspace_id", - "quantity", - "since", - "source" + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" ] } }, { "in": "query", - "name": "source", + "name": "subject_id", "schema": { - "type": "string", - "enum": [ - "direct", - "reviewed", - "self_checkout" - ] + "type": "integer" } - } - ], - "tags": [ - "Ledger" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "text/csv": { - "schema": { - "type": "string" - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { - "schema": { - "type": "string", - "format": "binary" - } - } - }, - "description": "" - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/loans/{id}/renew": { - "post": { - "operationId": "api_v1_admin_makerspace_loans_renew_create", - "summary": "Extend an issued loan within its policy", - "parameters": [ + }, { - "in": "path", - "name": "id", + "in": "query", + "name": "subject_kind", "schema": { - "type": "integer" + "type": "string" }, - "required": true + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "subject_type", "schema": { - "type": "integer" - }, - "required": true + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Loans" + "Analytics" ], - "requestBody": { - "content": { - "type": { - "schema": { - "type": "object", - "additionalProperties": {}, - "description": "Unspecified request body" - } - }, - "properties": { - "schema": {} - } - } - }, "security": [ { "jwtAuth": [] @@ -23378,7 +23135,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LoanRenewal" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -23388,182 +23145,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/logo": { - "post": { - "operationId": "api_v1_admin_makerspace_logo_create", - "summary": "Create a makerspace public image upload URL", + "/api/v1/admin/makerspace/{makerspace_id}/analytics/returns": { + "get": { + "operationId": "api_v1_admin_makerspace_analytics_returns_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "end", "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin makerspaces" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadRequest" - } + "type": "string", + "format": "date" } }, - "required": true - }, - "security": [ { - "jwtAuth": [] - } - ], - "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageUploadResponse" - } - } - }, - "description": "" - }, - "400": { - "description": "Invalid image upload request." - }, - "403": { - "description": "The actor lacks manage_makerspace here." - }, - "404": { - "description": "Makerspace not found, archived, not active, or outside the actor's scope." + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } }, - "503": { - "description": "Public image storage is unavailable." - } - } - }, - "put": { - "operationId": "api_v1_admin_makerspace_logo_update", - "summary": "Attach an uploaded public image to a makerspace", - "parameters": [ { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "limit", "schema": { "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin makerspaces" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicImageAttachRequest" - } } }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Makerspace" - } - } - }, - "description": "" - }, - "400": { - "description": "Invalid image object key or size." - }, - "403": { - "description": "The actor lacks manage_makerspace here." - }, - "404": { - "description": "Makerspace not found, archived, not active, or outside the actor's scope." - }, - "503": { - "description": "Public image storage is unavailable." - } - } - }, - "delete": { - "operationId": "api_v1_admin_makerspace_logo_destroy", - "summary": "Clear a makerspace public image", - "parameters": [ { "in": "path", "name": "makerspace_id", @@ -23571,44 +23224,18 @@ "type": "integer" }, "required": true - } - ], - "tags": [ - "Admin makerspaces" - ], - "security": [ + }, { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Makerspace" - } - } + "in": "query", + "name": "months", + "schema": { + "type": "integer" }, - "description": "" - }, - "403": { - "description": "The actor lacks manage_makerspace here." + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." }, - "404": { - "description": "Makerspace not found, archived, not active, or outside the actor's scope." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/machine-service-report": { - "get": { - "operationId": "api_v1_admin_makerspace_machine_service_report_retrieve", - "summary": "Retrieve makerspace machine-service report", - "parameters": [ { "in": "query", - "name": "end", + "name": "start", "schema": { "type": "string", "format": "date" @@ -23616,30 +23243,53 @@ }, { "in": "query", - "name": "machine_type", + "name": "status", "schema": { - "type": "string" + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] } }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "subject_id", "schema": { "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" }, - "required": true + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." }, { "in": "query", - "name": "start", + "name": "subject_type", "schema": { "type": "string", - "format": "date" + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] } } ], "tags": [ - "Admin machine service" + "Analytics" ], "security": [ { @@ -23651,7 +23301,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceReportResponse" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, @@ -23661,29 +23311,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Invalid request." + "description": "Invalid report request." }, "401": { - "description": "Authentication credentials were not provided." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/machine-type-pricing": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/summary": { "get": { - "operationId": "api_v1_admin_makerspace_machine_type_pricing_retrieve", - "summary": "List makerspace machine-type pricing", + "operationId": "api_v1_admin_makerspace_analytics_summary_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, { "in": "path", "name": "makerspace_id", @@ -23691,10 +23390,72 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Admin machines" + "Analytics" ], "security": [ { @@ -23706,30 +23467,87 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineTypePricingList" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid report request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." + }, "403": { - "description": "Space-manager identity required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/machine-type-pricing/{machine_type_id}": { - "put": { - "operationId": "api_v1_admin_makerspace_machine_type_pricing_update", - "summary": "Set makerspace machine-type pricing", + "/api/v1/admin/makerspace/{makerspace_id}/analytics/taken-items": { + "get": { + "operationId": "api_v1_admin_makerspace_analytics_taken_items_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ { - "in": "path", - "name": "machine_type_id", + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", "schema": { "type": "integer" - }, - "required": true + } }, { "in": "path", @@ -23738,31 +23556,73 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Admin machines" + "Analytics" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MachineTypePricingSet" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MachineTypePricingSet" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MachineTypePricingSet" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -23773,29 +23633,88 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineTypePricing" + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" }, "400": { - "description": "Invalid price." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid report request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Space-manager identity required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Machine type not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/machine-types": { + "/api/v1/admin/makerspace/{makerspace_id}/analytics/top-borrowers": { "get": { - "operationId": "api_v1_admin_makerspace_machine_types_list", - "summary": "List machine types for a makerspace", + "operationId": "api_v1_admin_makerspace_analytics_top_borrowers_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Get analytics report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, { "in": "path", "name": "makerspace_id", @@ -23803,10 +23722,72 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Admin machines" + "Analytics" ], "security": [ { @@ -23818,88 +23799,60 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineTypeAccess" - } + "$ref": "#/components/schemas/AnalyticsReportResponse" } } }, "description": "" - } - } - }, - "post": { - "operationId": "api_v1_admin_makerspace_machine_types_create", - "summary": "Create a custom machine type", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin machines" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MachineTypeCreate" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MachineTypeCreate" + "description": "Invalid report request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MachineTypeCreate" - } - } + "description": "Authentication required." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineType" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, - "400": { - "description": "Invalid machine type." + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/machine-types/{machine_type_id}/policy": { + "/api/v1/admin/makerspace/{makerspace_id}/api-client-scopes": { "get": { - "operationId": "api_v1_admin_makerspace_machine_types_policy_retrieve", - "summary": "Read machine-type reminder policy", + "operationId": "api_v1_admin_makerspace_api_client_scopes_retrieve", + "summary": "List API-client scope grant options", "parameters": [ - { - "in": "path", - "name": "machine_type_id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -23910,7 +23863,7 @@ } ], "tags": [ - "Admin machines" + "API clients" ], "security": [ { @@ -23922,69 +23875,71 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineTypePolicy" + "$ref": "#/components/schemas/ApiClientScopeCatalogResponse" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } - }, - "put": { - "operationId": "api_v1_admin_makerspace_machine_types_policy_update", - "summary": "Set machine-type reminder policy", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/api-clients": { + "get": { + "operationId": "api_v1_admin_makerspace_api_clients_list", + "summary": "List or create makerspace API clients", "parameters": [ { "in": "path", - "name": "machine_type_id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "in": "path", - "name": "makerspace_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Admin machines" + "API clients" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MachineTypePolicy" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MachineTypePolicy" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MachineTypePolicy" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -23995,43 +23950,48 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineTypePolicy" + "$ref": "#/components/schemas/PaginatedApiClientList" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/machine-types/{id}": { - "patch": { - "operationId": "api_v1_admin_makerspace_machine_types_partial_update", - "summary": "Update a custom machine type", + }, + "post": { + "operationId": "api_v1_admin_makerspace_api_clients_create", + "summary": "List or create makerspace API clients", "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -24042,26 +24002,27 @@ } ], "tags": [ - "Admin machines" + "API clients" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedMachineTypeUpdate" + "$ref": "#/components/schemas/ApiClient" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedMachineTypeUpdate" + "$ref": "#/components/schemas/ApiClient" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedMachineTypeUpdate" + "$ref": "#/components/schemas/ApiClient" } } - } + }, + "required": true }, "security": [ { @@ -24069,32 +24030,53 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineType" + "$ref": "#/components/schemas/ApiClientCreateResponse" } } }, "description": "" }, - "400": { - "description": "Invalid or built-in machine type." + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Machine type not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/machines": { + "/api/v1/admin/makerspace/{makerspace_id}/api-settings": { "get": { - "operationId": "api_v1_admin_makerspace_machines_retrieve", - "summary": "List machines in a makerspace", + "operationId": "api_v1_admin_makerspace_api_settings_retrieve", + "summary": "Retrieve API integration settings", "parameters": [ { "in": "path", @@ -24103,18 +24085,10 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "q", - "schema": { - "type": "string" - }, - "description": "Full-text search over name, location, firmware version and notes." } ], "tags": [ - "Admin machines" + "API clients" ], "security": [ { @@ -24126,17 +24100,26 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineListResponse" + "$ref": "#/components/schemas/ApiIntegrationSettings" } } }, "description": "" + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "The actor lacks manage_makerspace here." + }, + "404": { + "description": "Makerspace not found, archived, not active, or outside the actor's scope." } } }, - "post": { - "operationId": "api_v1_admin_makerspace_machines_create", - "summary": "Create a machine", + "patch": { + "operationId": "api_v1_admin_makerspace_api_settings_partial_update", + "summary": "Update API integration settings", "parameters": [ { "in": "path", @@ -24148,27 +24131,26 @@ } ], "tags": [ - "Admin machines" + "API clients" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/PatchedApiIntegrationSettings" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/PatchedApiIntegrationSettings" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/PatchedApiIntegrationSettings" } } - }, - "required": true + } }, "security": [ { @@ -24176,26 +24158,35 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Machine" + "$ref": "#/components/schemas/ApiIntegrationSettings" } } }, "description": "" }, "400": { - "description": "Invalid machine details." + "description": "Field validation errors, keyed by field." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "The actor lacks manage_makerspace here." + }, + "404": { + "description": "Makerspace not found, archived, not active, or outside the actor's scope." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/maintenance-overdue": { + "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients": { "get": { - "operationId": "api_v1_admin_makerspace_maintenance_overdue_list", - "summary": "List overdue maintenance in my machine scope", + "operationId": "api_v1_admin_makerspace_archive_recipients_list", + "summary": "List archive recipients for a makerspace", "parameters": [ { "in": "path", @@ -24204,19 +24195,10 @@ "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "Admin maintenance" + "Backup recipients" ], "security": [ { @@ -24228,31 +24210,61 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedOverdueScheduleList" + "type": "array", + "items": { + "$ref": "#/components/schemas/ArchiveRecipient" + } } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient or lifecycle request is invalid." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient does not exist in this makerspace." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Authentication is required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Manage-makerspace permission is required." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/membership-invitations": { + }, "post": { - "operationId": "api_v1_admin_makerspace_membership_invitations_create", - "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", + "operationId": "api_v1_admin_makerspace_archive_recipients_create", + "description": "The decrypted challenge is a 32-byte nonce exchanged as canonical, unpadded base64url. Only the SHA-256 digest of the raw 32 nonce bytes is persisted.", + "summary": "Enroll an archive recipient and issue a custody challenge", "parameters": [ { "in": "path", @@ -24264,23 +24276,23 @@ } ], "tags": [ - "Admin memberships" + "Backup recipients" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Invitation" + "$ref": "#/components/schemas/ArchiveRecipientCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Invitation" + "$ref": "#/components/schemas/ArchiveRecipientCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Invitation" + "$ref": "#/components/schemas/ArchiveRecipientCreate" } } }, @@ -24296,7 +24308,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipRequest" + "$ref": "#/components/schemas/ArchiveRecipientChallenge" } } }, @@ -24306,118 +24318,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ArchiveRecipientError" } } }, - "description": "" + "description": "The recipient or lifecycle request is invalid." }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ArchiveRecipientError" } } }, - "description": "" + "description": "The recipient does not exist in this makerspace." }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ArchiveRecipientError" } } }, - "description": "" + "description": "The recipient or lifecycle request is invalid." }, - "409": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ArchiveRecipientError" } } }, - "description": "" + "description": "The age challenge could not be encrypted." }, - "423": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ArchiveRecipientError" } } }, - "description": "" + "description": "Authentication is required." }, - "503": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ArchiveRecipientError" } } }, - "description": "" + "description": "Manage-makerspace permission is required." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/modules": { - "get": { - "operationId": "api_v1_admin_makerspace_modules_retrieve", - "summary": "List module groups and their install state for a makerspace", + "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/compromise": { + "post": { + "operationId": "api_v1_admin_makerspace_archive_recipients_compromise_create", + "summary": "Mark an archive recipient as compromised", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - } - ], - "tags": [ - "Platform" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "description": "Grouped module status." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/modules/install": { - "post": { - "operationId": "api_v1_admin_makerspace_modules_install_create", - "summary": "Install a module and everything it requires", - "parameters": [ { "in": "path", "name": "makerspace_id", @@ -24428,28 +24400,8 @@ } ], "tags": [ - "Platform" + "Backup recipients" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModuleAction" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ModuleAction" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ModuleAction" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -24457,26 +24409,71 @@ ], "responses": { "200": { - "description": "Keys newly installed." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipient" + } + } + }, + "description": "" }, "400": { - "description": "Unknown module, or not shipped by this deployment." - }, - "403": { - "description": "Active superadmin access is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient or lifecycle request is invalid." }, "404": { - "description": "Makerspace not found, archived, not active, or hidden from superadmins by its own manager." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient does not exist in this makerspace." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Authentication is required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Manage-makerspace permission is required." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/modules/uninstall": { + "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/reactivate": { "post": { - "operationId": "api_v1_admin_makerspace_modules_uninstall_create", - "description": "Clears the capability key only. Rows, uploads and history are retained and reinstalling restores every surface. Destroying the data is a separate, irreversible step (`purge_module_data`) that is deliberately CLI-only.", - "summary": "Uninstall a module, keeping its data", + "operationId": "api_v1_admin_makerspace_archive_recipients_reactivate_create", + "summary": "Reactivate a revoked archive recipient", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -24487,28 +24484,8 @@ } ], "tags": [ - "Platform" + "Backup recipients" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ModuleAction" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ModuleAction" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ModuleAction" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -24516,25 +24493,72 @@ ], "responses": { "200": { - "description": "Keys uninstalled." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipient" + } + } + }, + "description": "" }, "400": { - "description": "Core module, or required by an installed module." - }, - "403": { - "description": "Active superadmin access is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient or lifecycle request is invalid." }, "404": { - "description": "Makerspace not found, archived, not active, or hidden from superadmins by its own manager." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient does not exist in this makerspace." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Authentication is required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Manage-makerspace permission is required." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/notification-destinations": { - "get": { - "operationId": "api_v1_admin_makerspace_notification_destinations_list", - "summary": "List or create chat notification destinations", + "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/reissue-challenge": { + "post": { + "operationId": "api_v1_admin_makerspace_archive_recipients_reissue_challenge_create", + "description": "The decrypted 32-byte nonce is exchanged as canonical, unpadded base64url. The prior challenge stops verifying when this request commits.", + "summary": "Reissue an archive-recipient custody challenge", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -24545,7 +24569,7 @@ } ], "tags": [ - "Makerspaces" + "Backup recipients" ], "security": [ { @@ -24557,30 +24581,78 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationDestination" - } + "$ref": "#/components/schemas/ArchiveRecipientChallenge" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient or lifecycle request is invalid." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient does not exist in this makerspace." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The age challenge could not be encrypted." + }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Authentication is required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Manage-makerspace permission is required." } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/revoke": { "post": { - "operationId": "api_v1_admin_makerspace_notification_destinations_create", - "summary": "List or create chat notification destinations", + "operationId": "api_v1_admin_makerspace_archive_recipients_revoke_create", + "summary": "Revoke an archive recipient", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -24591,58 +24663,76 @@ } ], "tags": [ - "Makerspaces" + "Backup recipients" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/NotificationDestinationWrite" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/NotificationDestinationWrite" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/NotificationDestinationWrite" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotificationDestination" + "$ref": "#/components/schemas/ArchiveRecipient" } } }, "description": "" }, "400": { - "description": "Invalid destination." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient or lifecycle request is invalid." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient does not exist in this makerspace." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Authentication is required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Manage-makerspace permission is required." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/notification-destinations/{destination_id}": { - "put": { - "operationId": "api_v1_admin_makerspace_notification_destinations_update", - "summary": "Update or delete a chat notification destination", + "/api/v1/admin/makerspace/{makerspace_id}/archive-recipients/{id}/verify": { + "post": { + "operationId": "api_v1_admin_makerspace_archive_recipients_verify_create", + "description": "Submit the decrypted nonce as canonical unpadded base64url. It must decode to exactly 32 bytes; padding and non-canonical forms are refused.", + "summary": "Verify possession of an archive recipient", "parameters": [ { "in": "path", - "name": "destination_id", + "name": "id", "schema": { "type": "integer" }, @@ -24658,23 +24748,23 @@ } ], "tags": [ - "Makerspaces" + "Backup recipients" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotificationDestinationWrite" + "$ref": "#/components/schemas/ArchiveRecipientVerify" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/NotificationDestinationWrite" + "$ref": "#/components/schemas/ArchiveRecipientVerify" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/NotificationDestinationWrite" + "$ref": "#/components/schemas/ArchiveRecipientVerify" } } }, @@ -24690,66 +24780,79 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotificationDestination" + "$ref": "#/components/schemas/ArchiveRecipient" } } }, "description": "" }, "400": { - "description": "Invalid destination." - } - } - }, - "delete": { - "operationId": "api_v1_admin_makerspace_notification_destinations_destroy", - "summary": "Update or delete a chat notification destination", - "parameters": [ - { - "in": "path", - "name": "destination_id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } }, - "required": true + "description": "The recipient or lifecycle request is invalid." }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } }, - "required": true - } - ], - "tags": [ - "Makerspaces" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "Destination removed." + "description": "The recipient or lifecycle request is invalid." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "The recipient does not exist in this makerspace." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Verification attempts for this recipient are throttled." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Authentication is required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRecipientError" + } + } + }, + "description": "Manage-makerspace permission is required." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/notification-recipient-rules": { + "/api/v1/admin/makerspace/{makerspace_id}/archive-requests": { "get": { - "operationId": "api_v1_admin_makerspace_notification_recipient_rules_retrieve", - "summary": "Read or replace notification recipients", + "operationId": "api_v1_admin_makerspace_archive_requests_list", + "summary": "List archive requests for a makerspace", "parameters": [ { "in": "path", @@ -24761,7 +24864,7 @@ } ], "tags": [ - "Makerspaces" + "Admin makerspaces" ], "security": [ { @@ -24773,26 +24876,40 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecipientRulesResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/MakerspaceArchiveRequest" + } } } }, "description": "" }, - "401": { - "description": "Not signed in." - }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestError" + } + } + }, + "description": "" } } }, - "put": { - "operationId": "api_v1_admin_makerspace_notification_recipient_rules_update", - "summary": "Read or replace notification recipients", + "post": { + "operationId": "api_v1_admin_makerspace_archive_requests_create", + "summary": "Request makerspace archival", "parameters": [ { "in": "path", @@ -24804,23 +24921,23 @@ } ], "tags": [ - "Makerspaces" + "Admin makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecipientRulesPut" + "$ref": "#/components/schemas/MakerspaceArchiveRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/RecipientRulesPut" + "$ref": "#/components/schemas/MakerspaceArchiveRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/RecipientRulesPut" + "$ref": "#/components/schemas/MakerspaceArchiveRequest" } } }, @@ -24832,31 +24949,72 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecipientRulesResponse" + "$ref": "#/components/schemas/MakerspaceArchiveRequest" } } }, "description": "" }, "400": { - "description": "Invalid recipient rule." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestValidationError" + } + } + }, + "description": "The reason was blank or too long." }, "403": { - "description": "Recipient-rule permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/notification-recipients": { - "get": { - "operationId": "api_v1_admin_makerspace_notification_recipients_list", - "description": "Space-manager control over which of the makerspace's managers receive the staff\nlifecycle emails. Toggling a manager off clears `receives_notifications` without\ntouching their access/role.", - "summary": "List or toggle staff email notification recipients", + "/api/v1/admin/makerspace/{makerspace_id}/archive-requests/{id}/withdraw": { + "post": { + "operationId": "api_v1_admin_makerspace_archive_requests_withdraw_create", + "summary": "Withdraw a pending makerspace archive request", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -24867,7 +25025,7 @@ } ], "tags": [ - "Makerspaces" + "Admin makerspaces" ], "security": [ { @@ -24879,30 +25037,49 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationRecipient" - } + "$ref": "#/components/schemas/MakerspaceArchiveRequest" } } }, "description": "" }, - "401": { - "description": "Not signed in." - }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ArchiveRequestError" + } + } + }, + "description": "" } } - }, - "patch": { - "operationId": "api_v1_admin_makerspace_notification_recipients_partial_update", - "description": "Space-manager control over which of the makerspace's managers receive the staff\nlifecycle emails. Toggling a manager off clears `receives_notifications` without\ntouching their access/role.", - "summary": "List or toggle staff email notification recipients", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/audit-verifications": { + "get": { + "operationId": "api_v1_admin_makerspace_audit_verifications_retrieve", + "summary": "List audit verification runs for one makerspace", "parameters": [ { "in": "path", @@ -24911,30 +25088,19 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number (24 runs per page)." } ], "tags": [ - "Makerspaces" + "Audit verification" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedNotificationRecipientsPatch" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedNotificationRecipientsPatch" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedNotificationRecipientsPatch" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -24945,77 +25111,58 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationRecipient" - } + "$ref": "#/components/schemas/VerificationRunPage" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuditVerificationBadRequest" + } + } + }, + "description": "Invalid filters, `narrow_audit_range` or `audit_verification_scope_too_large`." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuditVerificationDetailError" + } + } + }, + "description": "Not authenticated." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/notification-rules": { - "get": { - "operationId": "api_v1_admin_makerspace_notification_rules_retrieve", - "summary": "List or update makerspace notification rules", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Makerspaces" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotificationRulesResponse" + "$ref": "#/components/schemas/AuditVerificationDetailError" } } }, - "description": "" - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Not active staff." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuditVerificationDetailError" + } + } + }, + "description": "Makerspace or run not in the caller's VIEW_AUDIT scope." } } }, - "patch": { - "operationId": "api_v1_admin_makerspace_notification_rules_partial_update", - "summary": "List or update makerspace notification rules", + "post": { + "operationId": "api_v1_admin_makerspace_audit_verifications_create", + "description": "Always asynchronous: 202 with the new or deduplicated run (`deduplicated`), then poll `status_url`. One active run per makerspace (409 `audit_verification_busy`); at most 10,000 selected rows and bounded expanded work (400); throttled per user (429).", + "summary": "Request asynchronous verification of the filtered audit rows", "parameters": [ { "in": "path", @@ -25027,23 +25174,23 @@ } ], "tags": [ - "Makerspaces" + "Audit verification" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedNotificationRulesPatch" + "$ref": "#/components/schemas/VerificationRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedNotificationRulesPatch" + "$ref": "#/components/schemas/VerificationRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedNotificationRulesPatch" + "$ref": "#/components/schemas/VerificationRequest" } } } @@ -25054,116 +25201,87 @@ } ], "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotificationRulesResponse" + "$ref": "#/components/schemas/VerificationAccepted" } } }, "description": "" }, - "400": { - "description": "Invalid notification rule or preference change." - }, - "404": { - "description": "Makerspace not found." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/payment-documents": { - "get": { - "operationId": "api_v1_admin_makerspace_payment_documents_retrieve", - "summary": "List scoped payment documents", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AuditVerificationError" + } + } }, - "required": true + "description": "`audit_verification_busy`." }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - } - ], - "tags": [ - "Payments" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DocumentPage" + "$ref": "#/components/schemas/AuditVerificationDetailError" } } }, - "description": "" + "description": "Throttled." }, "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AuditVerificationBadRequest" } } }, - "description": "" + "description": "Invalid filters, `narrow_audit_range` or `audit_verification_scope_too_large`." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AuditVerificationDetailError" } } }, - "description": "" + "description": "Not authenticated." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AuditVerificationDetailError" } } }, - "description": "" + "description": "Not active staff." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AuditVerificationDetailError" } } }, - "description": "" + "description": "Makerspace or run not in the caller's VIEW_AUDIT scope." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/payment-documents/{document_id}/download-url": { + "/api/v1/admin/makerspace/{makerspace_id}/audit-verifications/{run_id}": { "get": { - "operationId": "api_v1_admin_makerspace_payment_documents_download_url_retrieve", - "summary": "Get a user-bound five-minute PDF download URL", + "operationId": "api_v1_admin_makerspace_audit_verifications_retrieve_2", + "summary": "Read one audit verification run's latest state and coverage", "parameters": [ { "in": "path", - "name": "document_id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -25171,7 +25289,7 @@ }, { "in": "path", - "name": "makerspace_id", + "name": "run_id", "schema": { "type": "integer" }, @@ -25179,7 +25297,7 @@ } ], "tags": [ - "Payments" + "Audit verification" ], "security": [ { @@ -25191,7 +25309,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DownloadGrant" + "$ref": "#/components/schemas/VerificationRun" } } }, @@ -25201,49 +25319,49 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AuditVerificationBadRequest" } } }, - "description": "" + "description": "Invalid filters, `narrow_audit_range` or `audit_verification_scope_too_large`." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AuditVerificationDetailError" } } }, - "description": "" + "description": "Not authenticated." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AuditVerificationDetailError" } } }, - "description": "" + "description": "Not active staff." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AuditVerificationDetailError" } } }, - "description": "" + "description": "Makerspace or run not in the caller's VIEW_AUDIT scope." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/payment-settings": { + "/api/v1/admin/makerspace/{makerspace_id}/backup-custody": { "get": { - "operationId": "api_v1_admin_makerspace_payment_settings_retrieve", - "summary": "Retrieve makerspace payment settings", + "operationId": "api_v1_admin_makerspace_backup_custody_retrieve", + "summary": "Get the makerspace's archive and tenant-exit custody status", "parameters": [ { "in": "path", @@ -25255,7 +25373,7 @@ } ], "tags": [ - "Admin payment settings" + "Backup recipients" ], "security": [ { @@ -25267,37 +25385,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MakerspacePaymentSettings" + "$ref": "#/components/schemas/BackupCustody" } } }, "description": "" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentSettingsError" - } - } - }, - "description": "" + "400": { + "description": "The backup module is not installed for this makerspace." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentSettingsError" - } - } - }, - "description": "" + "description": "The requested resource does not exist in the actor's scope." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." } } - }, - "patch": { - "operationId": "api_v1_admin_makerspace_payment_settings_partial_update", - "summary": "Update makerspace payment settings", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/backups": { + "get": { + "operationId": "api_v1_admin_makerspace_backups_list", + "summary": "List makerspace backup archives", "parameters": [ { "in": "path", @@ -25309,27 +25421,8 @@ } ], "tags": [ - "Admin payment settings" + "Backup" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedMakerspacePaymentSettings" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedMakerspacePaymentSettings" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedMakerspacePaymentSettings" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -25340,50 +25433,32 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MakerspacePaymentSettings" + "type": "array", + "items": { + "$ref": "#/components/schemas/BackupArchive" + } } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Field errors, including a credential change blocked by a live session." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentSettingsError" - } - } - }, - "description": "" + "description": "The backup module is not installed for this makerspace." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentSettingsError" - } - } - }, - "description": "" + "description": "The requested resource does not exist in the actor's scope." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/payment-settings/connect/onboard": { + }, "post": { - "operationId": "api_v1_admin_makerspace_payment_settings_connect_onboard_create", - "summary": "Start Stripe Connect onboarding", + "operationId": "api_v1_admin_makerspace_backups_create", + "summary": "Request an age-encrypted makerspace backup", "parameters": [ { "in": "path", @@ -25395,7 +25470,7 @@ } ], "tags": [ - "Admin payment settings" + "Backup" ], "security": [ { @@ -25403,74 +25478,40 @@ } ], "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StripeConnectOnboarding" + "$ref": "#/components/schemas/BackupArchive" } } }, "description": "" }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentSettingsError" - } - } - }, - "description": "" + "400": { + "description": "The backup module is not installed for this makerspace." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentSettingsError" - } - } - }, - "description": "" + "description": "The requested resource does not exist in the actor's scope." }, "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentSettingsError" - } - } - }, - "description": "" + "description": "The backup worker is unavailable." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/payments": { + "/api/v1/admin/makerspace/{makerspace_id}/billing-settings": { "get": { - "operationId": "api_v1_admin_makerspace_payments_retrieve", - "description": "Requires view_finance. With for_recording=true, requires record_payment and returns only pending and offline-settled charges for counter receipt work.", - "summary": "List makerspace payments for reconciliation", + "operationId": "api_v1_admin_makerspace_billing_settings_retrieve", + "description": "Requires manage_makerspace. No payments module or credentials required; billing.reminders is a separate opt-in.", + "summary": "Read billing, receipt and accounting policy", "parameters": [ - { - "in": "query", - "name": "due_state", - "schema": { - "type": "string", - "enum": [ - "due_today", - "overdue", - "upcoming" - ] - } - }, - { - "in": "query", - "name": "for_recording", - "schema": { - "type": "boolean" - } - }, { "in": "path", "name": "makerspace_id", @@ -25478,53 +25519,6 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "ordering", - "schema": { - "type": "string" - }, - "description": "Allowlisted sort field; prefix with '-' for descending." - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - }, - "description": "Page number, 24 rows per page." - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ @@ -25540,7 +25534,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentListResponse" + "$ref": "#/components/schemas/BillingSettings" } } }, @@ -25550,11 +25544,11 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentFilterError" + "$ref": "#/components/schemas/BillingPolicyError" } } }, - "description": "" + "description": "Invalid billing policy fields." }, "401": { "content": { @@ -25564,7 +25558,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -25574,7 +25568,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -25584,26 +25578,14 @@ } } }, - "description": "Payment or makerspace not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment is already terminal." + "description": "" } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/amend-settlement": { - "post": { - "operationId": "api_v1_admin_makerspace_payments_amend_settlement_create", - "description": "Requires record_payment or the existing scoped payment-subject authority. Appends a correction; the original receipt remains immutable.", - "summary": "Correct how a settled payment was received", + }, + "patch": { + "operationId": "api_v1_admin_makerspace_billing_settings_partial_update", + "description": "Requires manage_makerspace. Terms apply only to new charges. Reminders require billing.reminders, notifications and email; no online rail required.", + "summary": "Change billing, receipt and accounting policy", "parameters": [ { "in": "path", @@ -25612,14 +25594,6 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "payment_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ @@ -25629,21 +25603,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentOffline" + "$ref": "#/components/schemas/PatchedBillingSettings" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PaymentOffline" + "$ref": "#/components/schemas/PatchedBillingSettings" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PaymentOffline" + "$ref": "#/components/schemas/PatchedBillingSettings" } } - }, - "required": true + } }, "security": [ { @@ -25655,7 +25628,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentReconciliation" + "$ref": "#/components/schemas/BillingSettings" } } }, @@ -25665,11 +25638,11 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/BillingPolicyError" } } }, - "description": "Invalid payment request." + "description": "Invalid billing policy fields." }, "401": { "content": { @@ -25679,7 +25652,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -25689,7 +25662,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -25699,26 +25672,15 @@ } } }, - "description": "Payment or makerspace not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment is already terminal." + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/mark-offline": { - "post": { - "operationId": "api_v1_admin_makerspace_payments_mark_offline_create", - "description": "Requires record_payment or the existing scoped payment-subject authority. Appends an audited manual settlement receipt.", - "summary": "Mark a payment paid offline", + "/api/v1/admin/makerspace/{makerspace_id}/categories": { + "get": { + "operationId": "api_v1_admin_makerspace_categories_list", + "summary": "List or create inventory categories", "parameters": [ { "in": "path", @@ -25729,37 +25691,18 @@ "required": true }, { - "in": "path", - "name": "payment_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Payments" + "Admin inventory" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentOffline" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PaymentOffline" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PaymentOffline" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -25770,70 +25713,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentReconciliation" + "$ref": "#/components/schemas/PaginatedCategoryAdminList" } } }, "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid payment request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment or makerspace not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment is already terminal." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/refund": { + }, "post": { - "operationId": "api_v1_admin_makerspace_payments_refund_create", - "description": "Sends money back through the provider that took it and records a Refund line on the payment. Only `paid_online` payments qualify; the sum of pending and succeeded refunds can never exceed the payment amount. The Payment row itself is immutable -- refund state lives on the Refund rows returned in `refunds`.", - "summary": "Refund a payment settled online, fully or partially", + "operationId": "api_v1_admin_makerspace_categories_create", + "summary": "List or create inventory categories", "parameters": [ { "in": "path", @@ -25842,34 +25732,26 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "payment_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Payments" + "Admin inventory" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentRefundRequest" + "$ref": "#/components/schemas/CategoryAdmin" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PaymentRefundRequest" + "$ref": "#/components/schemas/CategoryAdmin" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PaymentRefundRequest" + "$ref": "#/components/schemas/CategoryAdmin" } } }, @@ -25881,64 +25763,77 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentReconciliation" + "$ref": "#/components/schemas/CategoryAdmin" } } }, "description": "" + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/certification-types": { + "get": { + "operationId": "api_v1_admin_makerspace_certification_types_retrieve", + "summary": "List certification types in a makerspace", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true }, - "400": { + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number (24 rows per page)." + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/CertificationTypePage" } } }, - "description": "Refund not allowed or over the balance." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment or makerspace not found." - }, - "502": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "The provider rejected the refund." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/refund-offline": { + }, "post": { - "operationId": "api_v1_admin_makerspace_payments_refund_offline_create", - "description": "Requires active staff and record_payment. Records money already returned on a paid_offline charge, fully or partially, with method, optional financial reference and refunded_at. The receipt is append-only and audited; all pending/succeeded refunds share one balance. No provider call or payments module is required. The Payment remains unchanged.", - "summary": "Record an offline refund or release a cash loan deposit", + "operationId": "api_v1_admin_makerspace_certification_types_create", + "summary": "Create a certification type", "parameters": [ { "in": "path", @@ -25947,34 +25842,26 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "payment_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Payments" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentOfflineRefund" + "$ref": "#/components/schemas/CertificationType" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PaymentOfflineRefund" + "$ref": "#/components/schemas/CertificationType" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PaymentOfflineRefund" + "$ref": "#/components/schemas/CertificationType" } } }, @@ -25986,64 +25873,38 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentReconciliation" + "$ref": "#/components/schemas/CertificationType" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ManualRefundError" - } - } - }, - "description": "Invalid receipt, status or refund balance." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Record payment and active status required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment not found in the permitted makerspace." + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace frozen for tenant migration." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/refunds/{refund_id}/amend": { - "post": { - "operationId": "api_v1_admin_makerspace_payments_refunds_amend_create", - "description": "Requires record_payment. Replaces method, reference and returned date on the effective receipt; amount/currency and the original payment never change. No second refund event or transfer occurs.", - "summary": "Append a corrected manual refund receipt", + "/api/v1/admin/makerspace/{makerspace_id}/containers": { + "get": { + "operationId": "api_v1_admin_makerspace_containers_list", + "summary": "List containers", "parameters": [ { "in": "path", @@ -26054,45 +25915,27 @@ "required": true }, { - "in": "path", - "name": "payment_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } }, { - "in": "path", - "name": "refund_id", + "name": "page_size", + "required": false, + "in": "query", + "description": "Number of results to return per page.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Payments" + "Containers" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentRefundAmend" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PaymentRefundAmend" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PaymentRefundAmend" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -26103,7 +25946,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentReconciliation" + "$ref": "#/components/schemas/PaginatedBoxList" } } }, @@ -26113,7 +25956,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ManualRefundError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -26150,13 +25993,10 @@ "description": "" } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/release-deposit": { + }, "post": { - "operationId": "api_v1_admin_makerspace_payments_release_deposit_create", - "description": "Requires record_payment. Only a paid_offline loan deposit on a returned or closed_with_issue loan is eligible. Records actual money returned; never offsets fees or calls the rail.", - "summary": "Record a deposit returned after a loan closes", + "operationId": "api_v1_admin_makerspace_containers_create", + "summary": "Create container", "parameters": [ { "in": "path", @@ -26165,34 +26005,26 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "payment_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Payments" + "Containers" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentOfflineRefund" + "$ref": "#/components/schemas/Box" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PaymentOfflineRefund" + "$ref": "#/components/schemas/Box" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PaymentOfflineRefund" + "$ref": "#/components/schemas/Box" } } }, @@ -26204,11 +26036,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentReconciliation" + "$ref": "#/components/schemas/Box" } } }, @@ -26218,7 +26050,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ManualRefundError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -26257,10 +26089,10 @@ } } }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/waive": { + "/api/v1/admin/makerspace/{makerspace_id}/cover": { "post": { - "operationId": "api_v1_admin_makerspace_payments_waive_create", - "summary": "Waive a payment", + "operationId": "api_v1_admin_makerspace_cover_create", + "summary": "Create a makerspace public image upload URL", "parameters": [ { "in": "path", @@ -26269,93 +26101,64 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "payment_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Payments" + "Admin makerspaces" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentReconciliation" + "$ref": "#/components/schemas/PublicImageUploadResponse" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid payment request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Invalid image upload request." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "The actor lacks manage_makerspace here." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment or makerspace not found." + "description": "Makerspace not found, archived, not active, or outside the actor's scope." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment is already terminal." + "503": { + "description": "Public image storage is unavailable." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/bulk/mark-offline": { - "post": { - "operationId": "api_v1_admin_makerspace_payments_bulk_mark_offline_create", - "description": "Requires record_payment or the existing scoped payment-subject authority for every charge in the batch.", - "summary": "Mark payments paid offline in one transaction", + }, + "put": { + "operationId": "api_v1_admin_makerspace_cover_update", + "summary": "Attach an uploaded public image to a makerspace", "parameters": [ { "in": "path", @@ -26367,23 +26170,23 @@ } ], "tags": [ - "Payments" + "Admin makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentBulkOffline" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PaymentBulkOffline" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PaymentBulkOffline" + "$ref": "#/components/schemas/PublicImageAttachRequest" } } }, @@ -26399,36 +26202,102 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PaymentReconciliation" - } + "$ref": "#/components/schemas/Makerspace" } } }, "description": "" }, "400": { + "description": "Invalid image object key or size." + }, + "403": { + "description": "The actor lacks manage_makerspace here." + }, + "404": { + "description": "Makerspace not found, archived, not active, or outside the actor's scope." + }, + "503": { + "description": "Public image storage is unavailable." + } + } + }, + "delete": { + "operationId": "api_v1_admin_makerspace_cover_destroy", + "summary": "Clear a makerspace public image", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin makerspaces" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/Makerspace" } } }, - "description": "Invalid payment request." + "description": "" }, - "401": { + "403": { + "description": "The actor lacks manage_makerspace here." + }, + "404": { + "description": "Makerspace not found, archived, not active, or outside the actor's scope." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/dashboard": { + "get": { + "operationId": "api_v1_admin_makerspace_dashboard_retrieve", + "description": "Preserves the existing counters and adds report-backed widgets with chart specs. Widgets require their own actions and source modules plus reports; machine data uses assigned machine types. Handover-only actors receive queue tiles only. Native staff sessions use local membership authority, without organization grants.", + "summary": "Staff operations counts, action-scoped widgets and handover queues", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Dashboard" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/Dashboard" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -26436,9 +26305,9 @@ } } }, - "description": "Permission denied." + "description": "Authentication required." }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -26446,9 +26315,9 @@ } } }, - "description": "Payment or makerspace not found." + "description": "Permission denied." }, - "409": { + "404": { "content": { "application/json": { "schema": { @@ -26456,17 +26325,17 @@ } } }, - "description": "Payment is already terminal." + "description": "Makerspace not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/payments/bulk/waive": { - "post": { - "operationId": "api_v1_admin_makerspace_payments_bulk_waive_create", - "summary": "Waive payments in one transaction", - "parameters": [ - { + "/api/v1/admin/makerspace/{makerspace_id}/data-exports": { + "get": { + "operationId": "api_v1_admin_makerspace_data_exports_list", + "summary": "List makerspace data-export jobs", + "parameters": [ + { "in": "path", "name": "makerspace_id", "schema": { @@ -26476,27 +26345,66 @@ } ], "tags": [ - "Payments" + "Data exports" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DataExportJob" + } + } + } + }, + "description": "" + }, + "403": { + "description": "MANAGE_MAKERSPACE is required." + } + } + }, + "post": { + "operationId": "api_v1_admin_makerspace_data_exports_create", + "summary": "Request a redacted makerspace data export", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Data exports" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaymentBulkAction" + "$ref": "#/components/schemas/DataExportCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PaymentBulkAction" + "$ref": "#/components/schemas/DataExportCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PaymentBulkAction" + "$ref": "#/components/schemas/DataExportCreate" } } - }, - "required": true + } }, "security": [ { @@ -26504,87 +26412,41 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PaymentReconciliation" - } + "$ref": "#/components/schemas/DataExportJob" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid payment request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Invalid fidelity or quota/module policy failure." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment or makerspace not found." + "description": "MANAGE_MAKERSPACE is required." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Payment is already terminal." + "429": { + "description": "Export creation rate limit exceeded." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/pending-requests": { + "/api/v1/admin/makerspace/{makerspace_id}/data-exports/{job_id}": { "get": { - "operationId": "api_v1_admin_makerspace_pending_requests_list", - "summary": "List pending borrow requests", + "operationId": "api_v1_admin_makerspace_data_exports_retrieve", + "summary": "Poll a makerspace data-export job", "parameters": [ { - "name": "id", - "in": "query", - "required": false, - "description": "Select one result within the authorized list.", + "in": "path", + "name": "job_id", "schema": { - "type": "integer", - "minimum": 1, - "maximum": 9223372036854775807 - } + "type": "string", + "format": "uuid" + }, + "required": true }, { "in": "path", @@ -26593,37 +26455,10 @@ "type": "integer" }, "required": true - }, - { - "name": "ordering", - "required": false, - "in": "query", - "description": "Which field to use when ordering the results.", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } - }, - { - "name": "search", - "required": false, - "in": "query", - "description": "A search term (requested-for, requester name/email).", - "schema": { - "type": "string" - } } ], "tags": [ - "Admin requests" + "Data exports" ], "security": [ { @@ -26635,60 +26470,35 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedAdminRequestList" + "$ref": "#/components/schemas/DataExportJob" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." - }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "MANAGE_MAKERSPACE is required." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." + "description": "Export job not found in this makerspace." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/presence-sessions/current": { - "get": { - "operationId": "api_v1_admin_makerspace_presence_sessions_current_list", - "description": "Requires manage_members in this makerspace.", + "/api/v1/admin/makerspace/{makerspace_id}/data-exports/{job_id}/download-url": { + "post": { + "operationId": "api_v1_admin_makerspace_data_exports_download_url_create", + "summary": "Issue a short-lived one-use export download URL", "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -26699,7 +26509,7 @@ } ], "tags": [ - "Admin makerspaces" + "Data exports" ], "security": [ { @@ -26711,50 +26521,27 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PresenceRoster" - } + "$ref": "#/components/schemas/DataExportDownloadUrl" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid input." - }, - "401": { - "description": "Authentication required." + "description": "Export is not available." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Membership permission required." + "description": "MANAGE_MAKERSPACE is required; PORTABLE additionally requires source superadmin." }, "404": { - "description": "Makerspace not found." - }, - "429": { - "description": "Rate limit exceeded." + "description": "Export job not found in this makerspace." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/printer-jobs": { + "/api/v1/admin/makerspace/{makerspace_id}/device-printer-bindings": { "get": { - "operationId": "api_v1_admin_makerspace_printer_jobs_list", + "operationId": "api_v1_admin_makerspace_device_printer_bindings_list", "parameters": [ { "in": "path", @@ -26787,7 +26574,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedJobList" + "$ref": "#/components/schemas/PaginatedBindingList" } } }, @@ -26866,7 +26653,7 @@ } }, "post": { - "operationId": "api_v1_admin_makerspace_printer_jobs_create", + "operationId": "api_v1_admin_makerspace_device_printer_bindings_create", "parameters": [ { "in": "path", @@ -26884,17 +26671,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/JobCreate" + "$ref": "#/components/schemas/BindingCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/JobCreate" + "$ref": "#/components/schemas/BindingCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/JobCreate" + "$ref": "#/components/schemas/BindingCreate" } } }, @@ -26910,7 +26697,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Job" + "$ref": "#/components/schemas/Binding" } } }, @@ -26989,30 +26776,31 @@ } } }, - "/api/v1/admin/makerspace/{makerspace_id}/problem-reports/{id}/resolve": { - "post": { - "operationId": "api_v1_admin_makerspace_problem_reports_resolve_create", - "summary": "Resolve a public problem report", + "/api/v1/admin/makerspace/{makerspace_id}/devices": { + "get": { + "operationId": "api_v1_admin_makerspace_devices_list", + "summary": "List a makerspace's devices", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "in": "path", - "name": "makerspace_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Analytics" + "Devices" ], "security": [ { @@ -27024,29 +26812,68 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/PaginatedDeviceList" } } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Invalid request or module disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Not found." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/problem-reports/{id}/triage": { + }, "post": { - "operationId": "api_v1_admin_makerspace_problem_reports_triage_create", - "summary": "Triage a public problem report", + "operationId": "api_v1_admin_makerspace_devices_create", + "summary": "Create a pending device and its first enrollment", "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -27057,23 +26884,23 @@ } ], "tags": [ - "Analytics" + "Devices" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemReportTriage" + "$ref": "#/components/schemas/DeviceCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ProblemReportTriage" + "$ref": "#/components/schemas/DeviceCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ProblemReportTriage" + "$ref": "#/components/schemas/DeviceCreate" } } }, @@ -27085,24 +26912,72 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProblemReportTriageResponse" + "$ref": "#/components/schemas/DeviceCreated" } } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Invalid request or module disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Not found." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/profile": { + "/api/v1/admin/makerspace/{makerspace_id}/direct-loan-members": { "get": { - "operationId": "api_v1_admin_makerspace_profile_retrieve", - "description": "Core configuration: usable even with every optional module switched off.", - "summary": "Read location and opening hours", + "operationId": "api_v1_admin_makerspace_direct_loan_members_list", "parameters": [ { "in": "path", @@ -27111,10 +26986,19 @@ "type": "integer" }, "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } } ], "tags": [ - "Admin makerspaces" + "api" ], "security": [ { @@ -27126,27 +27010,19 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MakerspaceProfile" + "$ref": "#/components/schemas/PaginatedDirectLoanMemberList" } } }, "description": "" - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - }, - "patch": { - "operationId": "api_v1_admin_makerspace_profile_partial_update", - "description": "Requires manage_makerspace. Empty hours mean always open. Dated exceptions replace weekly hours. Currency updates the existing payment default, never currencies on existing charges.", - "summary": "Update location and opening hours", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/direct-loans": { + "get": { + "operationId": "api_v1_admin_makerspace_direct_loans_list", + "summary": "List direct handout loans", "parameters": [ { "in": "path", @@ -27155,30 +27031,20 @@ "type": "integer" }, "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } } ], "tags": [ - "Admin makerspaces" + "Admin requests" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedMakerspaceProfile" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedMakerspaceProfile" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedMakerspaceProfile" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -27189,7 +27055,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MakerspaceProfile" + "$ref": "#/components/schemas/PaginatedDirectLoanList" } } }, @@ -27199,48 +27065,57 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Invalid request." }, - "423": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileGateError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant writes are frozen for migration." + "description": "Authentication required." }, - "503": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileGateError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The source gate cannot prove writes are safe." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/provision-subdomain": { + }, "post": { - "operationId": "api_v1_admin_makerspace_provision_subdomain_create", - "summary": "Provision a platform subdomain for a makerspace", + "operationId": "api_v1_admin_makerspace_direct_loans_create", + "summary": "Issue direct handout without public request", "parameters": [ { "in": "path", @@ -27252,23 +27127,23 @@ } ], "tags": [ - "Admin makerspaces" + "Admin requests" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProvisionSubdomainRequest" + "$ref": "#/components/schemas/DirectLoanIssue" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ProvisionSubdomainRequest" + "$ref": "#/components/schemas/DirectLoanIssue" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ProvisionSubdomainRequest" + "$ref": "#/components/schemas/DirectLoanIssue" } } }, @@ -27280,11 +27155,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Makerspace" + "$ref": "#/components/schemas/DirectLoan" } } }, @@ -27294,39 +27169,69 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProvisionSubdomainValidationError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid or unavailable platform subdomain label." + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HostingError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Active superadmin access is required." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HostingError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace not found." + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/qr-print-batches": { + "/api/v1/admin/makerspace/{makerspace_id}/email-logs": { "get": { - "operationId": "api_v1_admin_makerspace_qr_print_batches_list", - "summary": "List QR print batches", + "operationId": "api_v1_admin_makerspace_email_logs_list", + "summary": "List makerspace email delivery logs", "parameters": [ { "in": "path", @@ -27344,10 +27249,25 @@ "schema": { "type": "integer" } + }, + { + "in": "query", + "name": "recent_days", + "schema": { + "type": "integer" + }, + "description": "Limit to the last 1–365 days." + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string" + } } ], "tags": [ - "QR print batches" + "Email logs" ], "security": [ { @@ -27359,18 +27279,40 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedQrPrintBatchList" + "$ref": "#/components/schemas/PaginatedEmailLogList" } } }, "description": "" + }, + "400": { + "description": "Invalid status filter." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Makerspace not found." } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/email-logs/{id}/retry": { "post": { - "operationId": "api_v1_admin_makerspace_qr_print_batches_create", - "summary": "Create QR print batch", + "operationId": "api_v1_admin_makerspace_email_logs_retry_create", + "summary": "Retry a failed makerspace email", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -27381,83 +27323,37 @@ } ], "tags": [ - "QR print batches" + "Email logs" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QrPrintBatchCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/QrPrintBatchCreate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/QrPrintBatchCreate" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrPrintBatch" + "$ref": "#/components/schemas/EmailLog" } } }, "description": "" + }, + "400": { + "description": "Email log cannot be retried." + }, + "404": { + "description": "Email log not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/reports/{report_key}/export": { + "/api/v1/admin/makerspace/{makerspace_id}/email-templates": { "get": { - "operationId": "api_v1_admin_makerspace_reports_export_retrieve", - "summary": "Export report", + "operationId": "api_v1_admin_makerspace_email_templates_list", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "format", - "schema": { - "type": "string", - "enum": [ - "csv", - "xlsx" - ] - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, { "in": "path", "name": "makerspace_id", @@ -27465,103 +27361,10 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, - { - "in": "path", - "name": "report_key", - "schema": { - "type": "string", - "enum": [ - "accounting-journal", - "active-loans", - "audit-trail", - "booking-utilization", - "cash-book", - "certification-coverage", - "communications-health", - "community-engagement", - "damaged-lost", - "damaged-missing", - "event-attendance", - "evidence-compliance", - "fablab-health", - "import-quality", - "inventory-control", - "loan-throughput", - "machine-service", - "machine-usage", - "maintenance-activity", - "member-activity", - "member-retention", - "membership-revenue", - "module-operational-health", - "most-lent", - "payment-cash-book", - "payment-reconciliation", - "payments-ledger", - "printer-service", - "procurement-performance", - "qr-scans", - "recently-added", - "returns", - "revenue-over-time", - "summary", - "taken-items", - "top-borrowers" - ] - }, - "required": true - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Reports" + "Email templates" ], "security": [ { @@ -27570,69 +27373,50 @@ ], "responses": { "200": { - "content": { - "text/csv": { - "schema": { - "type": "string" - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { - "schema": { - "type": "string", - "format": "binary" - } - } - }, - "description": "" - }, - "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailTemplateListItem" + } } } }, - "description": "Invalid report request." + "description": "" }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Makerspace or report not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/reports/catalog": { + "/api/v1/admin/makerspace/{makerspace_id}/email-templates/{stream}/{audience}/{key}": { "get": { - "operationId": "api_v1_admin_makerspace_reports_catalog_retrieve", - "summary": "List makerspace report catalog", + "operationId": "api_v1_admin_makerspace_email_templates_retrieve", "parameters": [ + { + "in": "path", + "name": "audience", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "key", + "schema": { + "type": "string" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -27640,10 +27424,18 @@ "type": "integer" }, "required": true + }, + { + "in": "path", + "name": "stream", + "schema": { + "type": "string" + }, + "required": true } ], "tags": [ - "Reports" + "Email templates" ], "security": [ { @@ -27655,110 +27447,84 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportCatalog" + "$ref": "#/components/schemas/EmailTemplateDetail" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Invalid report request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Makerspace or report not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/request-history": { - "get": { - "operationId": "api_v1_admin_makerspace_request_history_list", - "summary": "List terminal request history (returned / rejected / closed with issue)", + }, + "patch": { + "operationId": "api_v1_admin_makerspace_email_templates_partial_update", "parameters": [ - { - "name": "id", - "in": "query", - "required": false, - "description": "Select one result within the authorized list.", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 9223372036854775807 - } - }, { "in": "path", - "name": "makerspace_id", + "name": "audience", "schema": { - "type": "integer" + "type": "string" }, "required": true }, { - "name": "ordering", - "required": false, - "in": "query", - "description": "Which field to use when ordering the results.", + "in": "path", + "name": "key", "schema": { "type": "string" - } + }, + "required": true }, { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", + "in": "path", + "name": "makerspace_id", "schema": { "type": "integer" - } + }, + "required": true }, { - "name": "search", - "required": false, - "in": "query", - "description": "A search term (requested-for, requester name/email).", + "in": "path", + "name": "stream", "schema": { "type": "string" - } + }, + "required": true } ], "tags": [ - "Admin requests" + "Email templates" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -27769,59 +27535,47 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedAdminRequestList" + "$ref": "#/components/schemas/EmailTemplateDetail" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/return-policy": { - "get": { - "operationId": "api_v1_admin_makerspace_return_policy_retrieve", + "/api/v1/admin/makerspace/{makerspace_id}/email-templates/{stream}/{audience}/{key}/reset": { + "post": { + "operationId": "api_v1_admin_makerspace_email_templates_reset_create", "parameters": [ + { + "in": "path", + "name": "audience", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "key", + "schema": { + "type": "string" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -27829,10 +27583,18 @@ "type": "integer" }, "required": true + }, + { + "in": "path", + "name": "stream", + "schema": { + "type": "string" + }, + "required": true } ], "tags": [ - "Admin makerspaces" + "Email templates" ], "security": [ { @@ -27844,77 +27606,132 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReturnPolicy" + "$ref": "#/components/schemas/EmailTemplateDetail" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/email-templates/{stream}/{audience}/{key}/types/{machine_type_id}": { + "get": { + "operationId": "api_v1_admin_makerspace_email_templates_types_retrieve", + "parameters": [ + { + "in": "path", + "name": "audience", + "schema": { + "type": "string" }, - "description": "" + "required": true }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + { + "in": "path", + "name": "key", + "schema": { + "type": "string" }, - "description": "" + "required": true }, - "503": { + { + "in": "path", + "name": "machine_type_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "stream", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Email templates" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/EmailTemplateDetail" } } }, "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } }, "patch": { - "operationId": "api_v1_admin_makerspace_return_policy_partial_update", + "operationId": "api_v1_admin_makerspace_email_templates_types_partial_update", "parameters": [ + { + "in": "path", + "name": "audience", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "key", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "machine_type_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -27922,26 +27739,34 @@ "type": "integer" }, "required": true + }, + { + "in": "path", + "name": "stream", + "schema": { + "type": "string" + }, + "required": true } ], "tags": [ - "Admin makerspaces" + "Email templates" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedReturnPolicy" + "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedReturnPolicy" + "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedReturnPolicy" + "$ref": "#/components/schemas/PatchedEmailTemplateUpdate" } } } @@ -27956,79 +27781,109 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReturnPolicy" + "$ref": "#/components/schemas/EmailTemplateDetail" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/email-templates/{stream}/{audience}/{key}/types/{machine_type_id}/reset": { + "post": { + "operationId": "api_v1_admin_makerspace_email_templates_types_reset_create", + "parameters": [ + { + "in": "path", + "name": "audience", + "schema": { + "type": "string" }, - "description": "" + "required": true }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + { + "in": "path", + "name": "key", + "schema": { + "type": "string" }, - "description": "" + "required": true }, - "503": { + { + "in": "path", + "name": "machine_type_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "stream", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Email templates" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/EmailTemplateDetail" } } }, "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/setup-state": { - "get": { - "operationId": "api_v1_admin_makerspace_setup_state_retrieve", - "summary": "Read derived makerspace setup progress", + "/api/v1/admin/makerspace/{makerspace_id}/email-templates/preview": { + "post": { + "operationId": "api_v1_admin_makerspace_email_templates_preview_create", "parameters": [ { "in": "path", @@ -28040,8 +27895,28 @@ } ], "tags": [ - "Makerspaces" + "Email templates" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmailTemplatePreviewRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/EmailTemplatePreviewRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/EmailTemplatePreviewRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -28052,12 +27927,15 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetupState" + "$ref": "#/components/schemas/EmailTemplatePreviewResponse" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, "401": { "description": "Not signed in." }, @@ -28070,10 +27948,10 @@ } } }, - "/api/v1/admin/makerspace/{makerspace_id}/space-pack": { + "/api/v1/admin/makerspace/{makerspace_id}/integration-health": { "get": { - "operationId": "api_v1_admin_makerspace_space_pack_retrieve", - "summary": "Export configuration-only space pack", + "operationId": "api_v1_admin_makerspace_integration_health_retrieve", + "summary": "Get makerspace integration health", "parameters": [ { "in": "path", @@ -28085,7 +27963,7 @@ } ], "tags": [ - "Admin makerspaces" + "Integration health" ], "security": [ { @@ -28097,77 +27975,164 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SpacePack" + "$ref": "#/components/schemas/IntegrationHealth" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" - } - } + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Not allowed to manage this makerspace." + }, + "404": { + "description": "Makerspace not found." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/inventory": { + "get": { + "operationId": "api_v1_admin_makerspace_inventory_list", + "summary": "List or create inventory products", + "parameters": [ + { + "in": "query", + "name": "archived", + "schema": { + "type": "string" }, - "description": "" + "description": "`true` for archived, `false` for active; `all` or omitted includes both." }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" - } - } + { + "name": "id", + "in": "query", + "required": false, + "description": "Select one result within the authorized list.", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 9223372036854775807 + } + }, + { + "in": "query", + "name": "low_stock", + "schema": { + "type": "string" }, - "description": "" + "description": "`true` to list items at or below 20% of total." }, - "403": { + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "ordering", + "schema": { + "type": "string" + }, + "description": "Allowlisted sort field; prefix with '-' for descending." + }, + { + "in": "query", + "name": "out_of_stock", + "schema": { + "type": "string", + "enum": [ + "false", + "true" + ] + }, + "description": "`true` for exactly zero available, matching the dashboard tile." + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number, 24 rows per page." + }, + { + "name": "page_size", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Full-text search over name, storage location, tracking mode and description (\"phrases\", -exclusions, typo-tolerant on the name), or a category name." + } + ], + "tags": [ + "Admin inventory" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/PaginatedInventoryProductAdminList" } } }, "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "423": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "503": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -28176,9 +28141,8 @@ } }, "post": { - "operationId": "api_v1_admin_makerspace_space_pack_create", - "description": "Dry-run defaults true. Apply needs base_checksum from preview. Modules remain superadmin-only. Unrelated entries are retained.", - "summary": "Preview or atomically apply a space pack", + "operationId": "api_v1_admin_makerspace_inventory_create", + "summary": "List or create inventory products", "parameters": [ { "in": "path", @@ -28190,23 +28154,23 @@ } ], "tags": [ - "Admin makerspaces" + "Admin inventory" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackImport" + "$ref": "#/components/schemas/InventoryProductAdminCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PackImport" + "$ref": "#/components/schemas/InventoryProductAdminCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PackImport" + "$ref": "#/components/schemas/InventoryProductAdminCreate" } } }, @@ -28218,11 +28182,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackPreview" + "$ref": "#/components/schemas/InventoryProductAdmin" } } }, @@ -28232,7 +28196,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -28242,7 +28206,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -28252,7 +28216,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -28262,37 +28226,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -28301,11 +28235,48 @@ } } }, - "/api/v1/admin/makerspace/{makerspace_id}/stock-transfers": { + "/api/v1/admin/makerspace/{makerspace_id}/inventory/export": { "get": { - "operationId": "api_v1_admin_makerspace_stock_transfers_list", - "summary": "List stock transfers", + "operationId": "api_v1_admin_makerspace_inventory_export_retrieve", + "summary": "Export inventory products as CSV or XLSX", "parameters": [ + { + "in": "query", + "name": "archived", + "schema": { + "type": "string", + "enum": [ + "all", + "false", + "true" + ] + } + }, + { + "in": "query", + "name": "format", + "schema": { + "type": "string", + "enum": [ + "csv", + "xlsx" + ] + } + }, + { + "in": "query", + "name": "ids", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "low_stock", + "schema": { + "type": "boolean" + } + }, { "in": "path", "name": "makerspace_id", @@ -28315,17 +28286,27 @@ "required": true }, { - "name": "page", - "required": false, "in": "query", - "description": "A page number within the paginated result set.", + "name": "out_of_stock", "schema": { - "type": "integer" + "type": "string", + "enum": [ + "false", + "true" + ] + }, + "description": "Exactly zero available, matching the dashboard list." + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" } } ], "tags": [ - "Stock transfers" + "Admin inventory" ], "security": [ { @@ -28335,22 +28316,21 @@ "responses": { "200": { "content": { - "application/json": { + "text/csv": { "schema": { - "$ref": "#/components/schemas/PaginatedStockTransferList" + "type": "string" + } + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "schema": { + "type": "string", + "format": "binary" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GenericObject" - } - } - }, "description": "Invalid request." }, "401": { @@ -28363,10 +28343,12 @@ "description": "Not found." } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/inventory/import/apply": { "post": { - "operationId": "api_v1_admin_makerspace_stock_transfers_create", - "summary": "Create stock transfer", + "operationId": "api_v1_admin_makerspace_inventory_import_apply_create", + "summary": "Apply inventory bulk import", "parameters": [ { "in": "path", @@ -28378,23 +28360,90 @@ } ], "tags": [ - "Stock transfers" + "Bulk import" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StockTransferCreate" + "$ref": "#/components/schemas/BulkImportPreview" + }, + "examples": { + "PreviewInventoryRows": { + "value": { + "rows": [ + { + "name": "Soldering Iron", + "total_quantity": 10, + "available_quantity": 8, + "is_public": true + } + ], + "mapping": { + "name": "name", + "total_quantity": "total_quantity" + } + }, + "summary": "Preview inventory rows" + } } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/StockTransferCreate" + "$ref": "#/components/schemas/BulkImportPreview" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/StockTransferCreate" + "$ref": "#/components/schemas/BulkImportPreview" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "description": "Import application result." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/inventory/import/jobs": { + "post": { + "operationId": "api_v1_admin_makerspace_inventory_import_jobs_create", + "summary": "Create an async inventory bulk import job", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Bulk import" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkImportJobCreate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BulkImportJobCreate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BulkImportJobCreate" } } }, @@ -28410,38 +28459,28 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StockTransfer" + "$ref": "#/components/schemas/BulkImportJob" } } }, "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GenericObject" - } - } - }, - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/stocktake-policy": { + "/api/v1/admin/makerspace/{makerspace_id}/inventory/import/jobs/{job_id}": { "get": { - "operationId": "api_v1_admin_makerspace_stocktake_policy_retrieve", + "operationId": "api_v1_admin_makerspace_inventory_import_jobs_retrieve", + "summary": "Get async inventory bulk import job status", "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -28452,7 +28491,7 @@ } ], "tags": [ - "api" + "Bulk import" ], "security": [ { @@ -28464,43 +28503,146 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Policy" + "$ref": "#/components/schemas/BulkImportJob" } } }, "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/inventory/import/preview": { + "post": { + "operationId": "api_v1_admin_makerspace_inventory_import_preview_create", + "summary": "Preview inventory bulk import", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Bulk import" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BulkImportPreview" + }, + "examples": { + "PreviewInventoryRows": { + "value": { + "rows": [ + { + "name": "Soldering Iron", + "total_quantity": 10, + "available_quantity": 8, + "is_public": true + } + ], + "mapping": { + "name": "name", + "total_quantity": "total_quantity" + } + }, + "summary": "Preview inventory rows" } } }, - "description": "" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BulkImportPreview" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BulkImportPreview" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "description": "Import preview with row errors." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/inventory/reorder-draft": { + "post": { + "operationId": "api_v1_admin_makerspace_inventory_reorder_draft_create", + "summary": "Create or reuse a procurement reorder draft", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin inventory" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReorderDraft" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ReorderDraft" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ReorderDraft" + } + } }, - "403": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ToBuyItem" } } }, "description": "" }, - "404": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ToBuyItem" } } }, "description": "" }, - "423": { + "400": { "content": { "application/json": { "schema": { @@ -28510,7 +28652,7 @@ }, "description": "" }, - "429": { + "401": { "content": { "application/json": { "schema": { @@ -28520,7 +28662,7 @@ }, "description": "" }, - "503": { + "403": { "content": { "application/json": { "schema": { @@ -28530,21 +28672,23 @@ }, "description": "" }, - "400": { + "404": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" } } - }, - "put": { - "operationId": "api_v1_admin_makerspace_stocktake_policy_update", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/inventory/reorder-suggestions": { + "get": { + "operationId": "api_v1_admin_makerspace_inventory_reorder_suggestions_list", + "summary": "List products at their reorder points", "parameters": [ { "in": "path", @@ -28553,30 +28697,20 @@ "type": "integer" }, "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } } ], "tags": [ - "api" + "Admin inventory" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Policy" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/Policy" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/Policy" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -28587,33 +28721,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Policy" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaginatedReorderSuggestionList" } } }, "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { @@ -28623,7 +28737,7 @@ }, "description": "" }, - "423": { + "401": { "content": { "application/json": { "schema": { @@ -28633,7 +28747,7 @@ }, "description": "" }, - "429": { + "403": { "content": { "application/json": { "schema": { @@ -28643,7 +28757,7 @@ }, "description": "" }, - "503": { + "404": { "content": { "application/json": { "schema": { @@ -28652,24 +28766,15 @@ } }, "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/stocktake-schedules": { + "/api/v1/admin/makerspace/{makerspace_id}/kiosk-replays/": { "get": { - "operationId": "api_v1_admin_makerspace_stocktake_schedules_list", + "operationId": "api_v1_admin_makerspace_kiosk_replays_list", + "description": "Historical review remains available with inventory.kiosk disabled. No channel credentials or command payload are returned.", + "summary": "List failed handovers visible under current per-operation actions", "parameters": [ { "in": "path", @@ -28687,10 +28792,32 @@ "schema": { "type": "integer" } + }, + { + "in": "query", + "name": "resolution", + "schema": { + "type": "string", + "enum": [ + "all", + "resolved", + "unresolved" + ] + } + }, + { + "in": "query", + "name": "state", + "schema": { + "type": "string", + "enum": [ + "failed" + ] + } } ], "tags": [ - "api" + "Admin kiosk recovery" ], "security": [ { @@ -28702,13 +28829,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedScheduleList" + "$ref": "#/components/schemas/PaginatedReplayRecoveryList" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -28716,9 +28843,9 @@ } } }, - "description": "" + "description": "Invalid filter/resolution or disabled required module." }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -28726,9 +28853,9 @@ } } }, - "description": "" + "description": "Authentication required." }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -28736,9 +28863,9 @@ } } }, - "description": "" + "description": "Account, current action or origin refused." }, - "423": { + "404": { "content": { "application/json": { "schema": { @@ -28746,9 +28873,9 @@ } } }, - "description": "" + "description": "Failure is unavailable in your scope." }, - "429": { + "409": { "content": { "application/json": { "schema": { @@ -28756,9 +28883,9 @@ } } }, - "description": "" + "description": "Resolution/replacement conflict." }, - "503": { + "423": { "content": { "application/json": { "schema": { @@ -28766,23 +28893,15 @@ } } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" + "description": "Tenant source gate closed." } } - }, - "post": { - "operationId": "api_v1_admin_makerspace_stocktake_schedules_create", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/kiosk-stations/": { + "get": { + "operationId": "api_v1_admin_makerspace_kiosk_stations_list", + "summary": "List kiosk Stations", "parameters": [ { "in": "path", @@ -28794,45 +28913,28 @@ } ], "tags": [ - "api" + "Admin kiosk stations" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Schedule" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/Schedule" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/Schedule" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Schedule" + "type": "array", + "items": { + "$ref": "#/components/schemas/KioskStation" + } } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -28840,7 +28942,7 @@ } } }, - "description": "" + "description": "Feature disabled or invalid request." }, "403": { "content": { @@ -28850,7 +28952,7 @@ } } }, - "description": "" + "description": "Makerspace access or step-up denied." }, "404": { "content": { @@ -28860,9 +28962,9 @@ } } }, - "description": "" + "description": "Station not found." }, - "423": { + "409": { "content": { "application/json": { "schema": { @@ -28870,7 +28972,7 @@ } } }, - "description": "" + "description": "Rotate the PIN instead of revealing." }, "429": { "content": { @@ -28880,7 +28982,7 @@ } } }, - "description": "" + "description": "Request rate limit exceeded." }, "503": { "content": { @@ -28890,34 +28992,14 @@ } } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" + "description": "Credential secrets are unavailable." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/stocktake-schedules/{id}": { - "get": { - "operationId": "api_v1_admin_makerspace_stocktake_schedules_retrieve", + }, + "post": { + "operationId": "api_v1_admin_makerspace_kiosk_stations_create", + "summary": "Create a kiosk Station and its first PIN", "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -28928,25 +29010,45 @@ } ], "tags": [ - "api" + "Admin kiosk stations" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KioskStationCreate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/KioskStationCreate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/KioskStationCreate" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Schedule" + "$ref": "#/components/schemas/KioskStationSecret" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -28954,7 +29056,7 @@ } } }, - "description": "" + "description": "Feature disabled or invalid request." }, "403": { "content": { @@ -28964,7 +29066,7 @@ } } }, - "description": "" + "description": "Makerspace access or step-up denied." }, "404": { "content": { @@ -28974,9 +29076,9 @@ } } }, - "description": "" + "description": "Station not found." }, - "423": { + "409": { "content": { "application/json": { "schema": { @@ -28984,7 +29086,7 @@ } } }, - "description": "" + "description": "Rotate the PIN instead of revealing." }, "429": { "content": { @@ -28994,7 +29096,7 @@ } } }, - "description": "" + "description": "Request rate limit exceeded." }, "503": { "content": { @@ -29004,32 +29106,16 @@ } } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" + "description": "Credential secrets are unavailable." } } - }, - "patch": { - "operationId": "api_v1_admin_makerspace_stocktake_schedules_partial_update", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/kit-checklist-preview": { + "post": { + "operationId": "api_v1_admin_makerspace_kit_checklist_preview_create", + "summary": "Preview required issue or return kit components", "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -29040,23 +29126,23 @@ } ], "tags": [ - "api" + "Admin requests" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedSchedule" + "$ref": "#/components/schemas/KitPreviewInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedSchedule" + "$ref": "#/components/schemas/KitPreviewInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedSchedule" + "$ref": "#/components/schemas/KitPreviewInput" } } } @@ -29071,13 +29157,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Schedule" + "$ref": "#/components/schemas/KitPreview" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -29087,7 +29173,7 @@ }, "description": "" }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -29097,7 +29183,7 @@ }, "description": "" }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -29107,7 +29193,7 @@ }, "description": "" }, - "423": { + "404": { "content": { "application/json": { "schema": { @@ -29126,52 +29212,89 @@ } }, "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" } } - }, - "delete": { - "operationId": "api_v1_admin_makerspace_stocktake_schedules_destroy", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/ledger": { + "get": { + "operationId": "api_v1_admin_makerspace_ledger_retrieve", + "summary": "List outstanding inventory loans", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "overdue", + "schema": { + "type": "boolean" + } + }, + { + "in": "query", + "name": "page", "schema": { "type": "integer" - }, - "required": true + } + }, + { + "in": "query", + "name": "page_size", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "search", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string", + "enum": [ + "-due", + "-holder", + "-item_name", + "-makerspace_id", + "-quantity", + "-since", + "-source", + "due", + "holder", + "item_name", + "makerspace_id", + "quantity", + "since", + "source" + ] + } + }, + { + "in": "query", + "name": "source", + "schema": { + "type": "string", + "enum": [ + "direct", + "reviewed", + "self_checkout" + ] + } } ], "tags": [ - "api" + "Ledger" ], "security": [ { @@ -29179,75 +29302,11 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "400": { + "200": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/LedgerResponse" } } }, @@ -29256,11 +29315,22 @@ } } }, - "/api/v1/admin/makerspace/{makerspace_id}/stocktakes": { + "/api/v1/admin/makerspace/{makerspace_id}/ledger/export": { "get": { - "operationId": "api_v1_admin_makerspace_stocktakes_list", - "summary": "List stocktakes", + "operationId": "api_v1_admin_makerspace_ledger_export_retrieve", + "summary": "Export outstanding inventory loans", "parameters": [ + { + "in": "query", + "name": "format", + "schema": { + "type": "string", + "enum": [ + "csv", + "xlsx" + ] + } + }, { "in": "path", "name": "makerspace_id", @@ -29270,32 +29340,57 @@ "required": true }, { - "name": "page", - "required": false, "in": "query", - "description": "A page number within the paginated result set.", + "name": "overdue", "schema": { - "type": "integer" + "type": "boolean" } }, { "in": "query", - "name": "status", + "name": "search", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "sort", "schema": { "type": "string", "enum": [ - "applied", - "approved", - "cancelled", - "completed", - "counting", - "draft" + "-due", + "-holder", + "-item_name", + "-makerspace_id", + "-quantity", + "-since", + "-source", + "due", + "holder", + "item_name", + "makerspace_id", + "quantity", + "since", + "source" + ] + } + }, + { + "in": "query", + "name": "source", + "schema": { + "type": "string", + "enum": [ + "direct", + "reviewed", + "self_checkout" ] } } ], "tags": [ - "Stocktake" + "Ledger" ], "security": [ { @@ -29305,39 +29400,36 @@ "responses": { "200": { "content": { - "application/json": { + "text/csv": { "schema": { - "$ref": "#/components/schemas/PaginatedStocktakeList" + "type": "string" } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { "schema": { - "$ref": "#/components/schemas/GenericObject" + "type": "string", + "format": "binary" } } }, - "description": "Invalid request or stocktake state." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "" } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/loans/{id}/renew": { "post": { - "operationId": "api_v1_admin_makerspace_stocktakes_create", - "summary": "Create stocktake", + "operationId": "api_v1_admin_makerspace_loans_renew_create", + "summary": "Extend an issued loan within its policy", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -29348,24 +29440,19 @@ } ], "tags": [ - "Stocktake" + "Loans" ], "requestBody": { "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/StocktakeCreate" - } - }, - "application/x-www-form-urlencoded": { + "type": { "schema": { - "$ref": "#/components/schemas/StocktakeCreate" + "type": "object", + "additionalProperties": {}, + "description": "Unspecified request body" } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/StocktakeCreate" - } + "properties": { + "schema": {} } } }, @@ -29375,11 +29462,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Stocktake" + "$ref": "#/components/schemas/LoanRenewal" } } }, @@ -29389,28 +29476,49 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GenericObject" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request or stocktake state." + "description": "" }, "401": { - "description": "Authentication credentials were not provided." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Permission denied." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found." - } - } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/subdomain-request": { - "get": { - "operationId": "api_v1_admin_makerspace_subdomain_request_list", - "summary": "List platform subdomain requests for a makerspace", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/logo": { + "post": { + "operationId": "api_v1_admin_makerspace_logo_create", + "summary": "Create a makerspace public image upload URL", "parameters": [ { "in": "path", @@ -29419,61 +29527,64 @@ "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "Admin hosting" + "Admin makerspaces" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedSubdomainRequestList" + "$ref": "#/components/schemas/PublicImageUploadResponse" } } }, "description": "" }, + "400": { + "description": "Invalid image upload request." + }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubdomainRequestError" - } - } - }, - "description": "" + "description": "The actor lacks manage_makerspace here." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubdomainRequestError" - } - } - }, - "description": "" + "description": "Makerspace not found, archived, not active, or outside the actor's scope." + }, + "503": { + "description": "Public image storage is unavailable." } } }, - "post": { - "operationId": "api_v1_admin_makerspace_subdomain_request_create", - "summary": "Request a platform subdomain for a makerspace", + "put": { + "operationId": "api_v1_admin_makerspace_logo_update", + "summary": "Attach an uploaded public image to a makerspace", "parameters": [ { "in": "path", @@ -29485,23 +29596,23 @@ } ], "tags": [ - "Admin hosting" + "Admin makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubdomainRequest" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/SubdomainRequest" + "$ref": "#/components/schemas/PublicImageAttachRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/SubdomainRequest" + "$ref": "#/components/schemas/PublicImageAttachRequest" } } }, @@ -29513,53 +29624,33 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubdomainRequest" + "$ref": "#/components/schemas/Makerspace" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubdomainRequestError" - } - } - }, - "description": "" + "description": "Invalid image object key or size." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubdomainRequestError" - } - } - }, - "description": "" + "description": "The actor lacks manage_makerspace here." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubdomainRequestError" - } - } - }, - "description": "" + "description": "Makerspace not found, archived, not active, or outside the actor's scope." + }, + "503": { + "description": "Public image storage is unavailable." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/disclosure-approvals": { - "get": { - "operationId": "api_v1_admin_makerspace_tenant_migration_disclosure_approvals_list", - "summary": "List disclosure approvals", + }, + "delete": { + "operationId": "api_v1_admin_makerspace_logo_destroy", + "summary": "Clear a makerspace public image", "parameters": [ { "in": "path", @@ -29571,7 +29662,7 @@ } ], "tags": [ - "Tenant migration" + "Admin makerspaces" ], "security": [ { @@ -29583,61 +29674,41 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ClosureApproval" - } + "$ref": "#/components/schemas/Makerspace" } } }, "description": "" }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." - }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." + "description": "The actor lacks manage_makerspace here." }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "404": { + "description": "Makerspace not found, archived, not active, or outside the actor's scope." } } - }, - "post": { - "operationId": "api_v1_admin_makerspace_tenant_migration_disclosure_approvals_create", - "summary": "Approve each identity in one exact disclosure closure", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/machine-service-report": { + "get": { + "operationId": "api_v1_admin_makerspace_machine_service_report_retrieve", + "summary": "Retrieve makerspace machine-service report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "machine_type", + "schema": { + "type": "string" + } + }, { "in": "path", "name": "makerspace_id", @@ -29645,42 +29716,30 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } } ], "tags": [ - "Tenant migration" + "Admin machine service" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ClosureApprovalCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ClosureApprovalCreate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ClosureApprovalCreate" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ClosureApproval" + "$ref": "#/components/schemas/MachineServiceReportResponse" } } }, @@ -29690,79 +29749,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FieldValidationError" - } - } - }, - "description": "Field-keyed validation errors." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "State conflict." + "description": "Invalid request." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." + "description": "Machine management permission required." }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "404": { + "description": "Not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/disclosure-approvals/{approval_id}/revoke": { - "post": { - "operationId": "api_v1_admin_makerspace_tenant_migration_disclosure_approvals_revoke_create", - "summary": "Revoke a disclosure approval", + "/api/v1/admin/makerspace/{makerspace_id}/machine-type-pricing": { + "get": { + "operationId": "api_v1_admin_makerspace_machine_type_pricing_retrieve", + "summary": "List makerspace machine-type pricing", "parameters": [ - { - "in": "path", - "name": "approval_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -29773,7 +29782,7 @@ } ], "tags": [ - "Tenant migration" + "Admin machines" ], "security": [ { @@ -29785,70 +29794,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ClosureApproval" + "$ref": "#/components/schemas/MachineTypePricingList" } } }, "description": "" }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Not found." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." - }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "description": "Space-manager identity required." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/disclosure-closure": { - "get": { - "operationId": "api_v1_admin_makerspace_tenant_migration_disclosure_closure_retrieve", - "summary": "Compute the pending PORTABLE disclosure closure", + "/api/v1/admin/makerspace/{makerspace_id}/machine-type-pricing/{machine_type_id}": { + "put": { + "operationId": "api_v1_admin_makerspace_machine_type_pricing_update", + "summary": "Set makerspace machine-type pricing", "parameters": [ + { + "in": "path", + "name": "machine_type_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -29859,8 +29829,28 @@ } ], "tags": [ - "Tenant migration" + "Admin machines" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MachineTypePricingSet" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MachineTypePricingSet" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MachineTypePricingSet" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -29871,59 +29861,28 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PendingClosure" + "$ref": "#/components/schemas/MachineTypePricing" } } }, "description": "" }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." + "400": { + "description": "Invalid price." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." + "description": "Space-manager identity required." }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "404": { + "description": "Machine type not found." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/exports": { + "/api/v1/admin/makerspace/{makerspace_id}/machine-types": { "get": { - "operationId": "api_v1_admin_makerspace_tenant_migration_exports_list", - "summary": "List PORTABLE migration export jobs", + "operationId": "api_v1_admin_makerspace_machine_types_list", + "summary": "List machine types for a makerspace", "parameters": [ { "in": "path", @@ -29935,7 +29894,7 @@ } ], "tags": [ - "Tenant migration" + "Admin machines" ], "security": [ { @@ -29949,58 +29908,18 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/MigrationExportJob" + "$ref": "#/components/schemas/MachineTypeAccess" } } } }, "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." } } }, "post": { - "operationId": "api_v1_admin_makerspace_tenant_migration_exports_create", - "summary": "Create a target-recipient-encrypted PORTABLE export", + "operationId": "api_v1_admin_makerspace_machine_types_create", + "summary": "Create a custom machine type", "parameters": [ { "in": "path", @@ -30012,23 +29931,23 @@ } ], "tags": [ - "Tenant migration" + "Admin machines" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MigrationExportCreate" + "$ref": "#/components/schemas/MachineTypeCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MigrationExportCreate" + "$ref": "#/components/schemas/MachineTypeCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MigrationExportCreate" + "$ref": "#/components/schemas/MachineTypeCreate" } } }, @@ -30040,93 +29959,32 @@ } ], "responses": { - "202": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MigrationExportJob" + "$ref": "#/components/schemas/MachineType" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FieldValidationError" - } - } - }, - "description": "Field-keyed validation errors." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "State conflict." - }, - "503": { - "description": "Migration worker unavailable." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "description": "Invalid machine type." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/exports/{job_id}": { + "/api/v1/admin/makerspace/{makerspace_id}/machine-types/{machine_type_id}/policy": { "get": { - "operationId": "api_v1_admin_makerspace_tenant_migration_exports_retrieve", - "summary": "Read a PORTABLE migration export job", + "operationId": "api_v1_admin_makerspace_machine_types_policy_retrieve", + "summary": "Read machine-type reminder policy", "parameters": [ { "in": "path", - "name": "job_id", + "name": "machine_type_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true }, @@ -30140,7 +29998,7 @@ } ], "tags": [ - "Tenant migration" + "Admin machines" ], "security": [ { @@ -30152,76 +30010,35 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MigrationExportJob" + "$ref": "#/components/schemas/MachineTypePolicy" } } }, "description": "" }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Not found." + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/exports/{job_id}/download-url": { - "post": { - "operationId": "api_v1_admin_makerspace_tenant_migration_exports_download_url_create", - "summary": "Issue the one-use PORTABLE archive download URL", + }, + "put": { + "operationId": "api_v1_admin_makerspace_machine_types_policy_update", + "summary": "Set machine-type reminder policy", "parameters": [ { "in": "path", - "name": "job_id", + "name": "machine_type_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true }, @@ -30235,8 +30052,27 @@ } ], "tags": [ - "Tenant migration" + "Admin machines" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MachineTypePolicy" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MachineTypePolicy" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MachineTypePolicy" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -30247,86 +30083,40 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DataExportDownloadUrl" + "$ref": "#/components/schemas/MachineTypePolicy" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "State conflict." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Not found." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "423": { + "description": "Makerspace writes are frozen for tenant migration." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/exports/{job_id}/quiesce": { - "post": { - "operationId": "api_v1_admin_makerspace_tenant_migration_exports_quiesce_create", - "summary": "Reassert a completed export's source gate lease", + "/api/v1/admin/makerspace/{makerspace_id}/machine-types/{id}": { + "patch": { + "operationId": "api_v1_admin_makerspace_machine_types_partial_update", + "summary": "Update a custom machine type", "parameters": [ { "in": "path", - "name": "job_id", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true }, @@ -30340,8 +30130,27 @@ } ], "tags": [ - "Tenant migration" + "Admin machines" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedMachineTypeUpdate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedMachineTypeUpdate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedMachineTypeUpdate" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -30352,79 +30161,129 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CutoverOutcome" + "$ref": "#/components/schemas/MachineType" } } }, "description": "" }, + "400": { + "description": "Invalid or built-in machine type." + }, + "403": { + "description": "Machine management permission required." + }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } + "description": "Machine type not found." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/machines": { + "get": { + "operationId": "api_v1_admin_makerspace_machines_retrieve", + "summary": "List machines in a makerspace", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, - "description": "Not found." + "required": true }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } + { + "in": "query", + "name": "q", + "schema": { + "type": "string" }, - "description": "State conflict." - }, - "401": { + "description": "Full-text search over name, location, firmware version and notes." + } + ], + "tags": [ + "Admin machines" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/MachineListResponse" } } }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } + "description": "" + } + } + }, + "post": { + "operationId": "api_v1_admin_makerspace_machines_create", + "summary": "Create a machine", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machines" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Machine" } }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Machine" } }, - "description": "Throttle limit exceeded." + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Machine" + } + } }, - "500": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/Machine" } } }, - "description": "Unexpected migration failure." + "description": "" + }, + "400": { + "description": "Invalid machine details." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/pairings/{pairing_id}/archive-source": { - "post": { - "operationId": "api_v1_admin_makerspace_tenant_migration_pairings_archive_source_create", - "summary": "Archive the quiesced source and issue its signed cutover receipt", + "/api/v1/admin/makerspace/{makerspace_id}/maintenance-overdue": { + "get": { + "operationId": "api_v1_admin_makerspace_maintenance_overdue_list", + "summary": "List overdue maintenance in my machine scope", "parameters": [ { "in": "path", @@ -30435,17 +30294,17 @@ "required": true }, { - "in": "path", - "name": "pairing_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "type": "integer" + } } ], "tags": [ - "Tenant migration" + "Admin maintenance" ], "security": [ { @@ -30457,79 +30316,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CutoverOutcome" + "$ref": "#/components/schemas/PaginatedOverdueScheduleList" } } }, "description": "" }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "State conflict." + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/pairings/{pairing_id}/recover": { + "/api/v1/admin/makerspace/{makerspace_id}/membership-invitations": { "post": { - "operationId": "api_v1_admin_makerspace_tenant_migration_pairings_recover_create", - "summary": "Recover an archived source with the target abort receipt", + "operationId": "api_v1_admin_makerspace_membership_invitations_create", + "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", "parameters": [ { "in": "path", @@ -30538,35 +30349,26 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "pairing_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true } ], "tags": [ - "Tenant migration" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CutoverReceiptRequest" + "$ref": "#/components/schemas/Invitation" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CutoverReceiptRequest" + "$ref": "#/components/schemas/Invitation" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CutoverReceiptRequest" + "$ref": "#/components/schemas/Invitation" } } }, @@ -30578,11 +30380,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CutoverOutcome" + "$ref": "#/components/schemas/MembershipRequest" } } }, @@ -30592,79 +30394,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FieldValidationError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Field-keyed validation errors." + "description": "" }, - "404": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found." + "description": "" }, - "409": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "State conflict." + "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "" } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/usage": { + "/api/v1/admin/makerspace/{makerspace_id}/messaging-settings": { "get": { - "operationId": "api_v1_admin_makerspace_usage_retrieve", - "summary": "Read own makerspace usage and effective limits", + "operationId": "api_v1_admin_makerspace_messaging_settings_retrieve", "parameters": [ { "in": "path", @@ -30676,7 +30477,7 @@ } ], "tags": [ - "Admin makerspaces" + "Messaging" ], "security": [ { @@ -30688,7 +30489,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MakerspaceUsage" + "$ref": "#/components/schemas/MessagingSettings" } } }, @@ -30707,12 +30508,9 @@ "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - } - }, - "/api/v1/admin/makerspace/{makerspace_id}/verify-domain": { - "post": { - "operationId": "api_v1_admin_makerspace_verify_domain_create", - "summary": "Verify a makerspace custom domain", + }, + "patch": { + "operationId": "api_v1_admin_makerspace_messaging_settings_partial_update", "parameters": [ { "in": "path", @@ -30724,8 +30522,27 @@ } ], "tags": [ - "Admin makerspaces" + "Messaging" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedMessagingSettings" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedMessagingSettings" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedMessagingSettings" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -30736,35 +30553,41 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DomainVerificationResponse" + "$ref": "#/components/schemas/MessagingSettings" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, "403": { - "description": "Missing makerspace management permission." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "description": "Makerspace not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/admin/makerspace/{makerspace_id}/warranties": { - "get": { - "operationId": "api_v1_admin_makerspace_warranties_list", - "summary": "List warranty coverage for a makerspace", + "/api/v1/admin/makerspace/{makerspace_id}/messaging-settings/test": { + "post": { + "operationId": "api_v1_admin_makerspace_messaging_settings_test_create", + "description": "Diagnostic to your own verified, consented phone. The chosen event must pass feature/matrix, channel, consent, suppression, template and quota gates.", "parameters": [ - { - "in": "query", - "name": "expires_before", - "schema": { - "type": "string", - "format": "date" - }, - "description": "Only include hosts with warranty expiry on or before this date." - }, { "in": "path", "name": "makerspace_id", @@ -30772,106 +30595,84 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "missing_docs", - "schema": { - "type": "boolean" - }, - "description": "Only include hosts with no warranty documents or no warranty record." - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } - }, - { - "name": "page_size", - "required": false, - "in": "query", - "description": "Number of results to return per page.", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "active", - "expired", - "expiring_soon", - "unknown" - ] - }, - "description": "Filter rows by computed warranty status." } ], "tags": [ - "Admin warranty" + "Messaging" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MessagingTest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MessagingTest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MessagingTest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedWarrantyReportRowList" + "$ref": "#/components/schemas/MessagingTestResponse" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." } } } }, - "/api/v1/admin/makerspaces": { + "/api/v1/admin/makerspace/{makerspace_id}/modules": { "get": { - "operationId": "api_v1_admin_makerspaces_list", - "summary": "List makerspaces the actor has a staff surface in", + "operationId": "api_v1_admin_makerspace_modules_retrieve", + "summary": "List module groups and their install state for a makerspace", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Admin makerspaces" + "Platform" ], "security": [ { @@ -30880,45 +30681,52 @@ ], "responses": { "200": { - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Makerspace" - } - } - } - }, - "description": "" + "description": "Grouped module status." + }, + "401": { + "description": "Not signed in." }, "403": { - "description": "Not an active staff account." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/modules/install": { "post": { - "operationId": "api_v1_admin_makerspaces_create", - "description": "Optional install_profile_id applies a saved pack. creation_setup applies profile, hours, modules and first staff invitation in the same transaction; explicit setup overrides the pack. Invitation delivery follows commit and returns a private fallback link.", - "summary": "Create a makerspace (superadmin only)", + "operationId": "api_v1_admin_makerspace_modules_install_create", + "summary": "Install a module and everything it requires", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Admin makerspaces" + "Platform" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Makerspace" + "$ref": "#/components/schemas/ModuleAction" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Makerspace" + "$ref": "#/components/schemas/ModuleAction" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Makerspace" + "$ref": "#/components/schemas/ModuleAction" } } }, @@ -30930,85 +30738,26 @@ } ], "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Makerspace" - } - } - }, - "description": "" + "200": { + "description": "Keys newly installed." }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {}, - "properties": { - "theme_config": { - "oneOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "object", - "required": [ - "code", - "messages" - ], - "properties": { - "code": { - "type": "string", - "enum": [ - "theme_contrast" - ] - }, - "messages": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - ] - } - } - } - } - }, - "description": "Field validation errors. Contrast errors have theme_config: {code: 'theme_contrast', messages: string[]} with mode, failing pair, measured ratio and required ratio." + "description": "Unknown module, or not shipped by this deployment." }, - "401": { - "description": "Not signed in." + "403": { + "description": "Active superadmin access is required." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "409": { - "description": "Conflicts with the current state." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." - }, - "503": { - "description": "A required dependency is unavailable." - }, - "403": { - "description": "Only a superadmin can create a makerspace." + "description": "Makerspace not found, archived, not active, or hidden from superadmins by its own manager." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/booking-machine-types/": { - "get": { - "operationId": "api_v1_admin_makerspaces_booking_machine_types_retrieve", - "summary": "List machine type choices for booking spaces", + "/api/v1/admin/makerspace/{makerspace_id}/modules/uninstall": { + "post": { + "operationId": "api_v1_admin_makerspace_modules_uninstall_create", + "description": "Clears the capability key only. Rows, uploads and history are retained and reinstalling restores every surface. Destroying the data is a separate, irreversible step (`purge_module_data`) that is deliberately CLI-only.", + "summary": "Uninstall a module, keeping its data", "parameters": [ { "in": "path", @@ -31017,19 +30766,31 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - }, - "description": "Page number." } ], "tags": [ - "Admin bookings" + "Platform" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModuleAction" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ModuleAction" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ModuleAction" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -31037,62 +30798,24 @@ ], "responses": { "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BookingMachineTypePage" - } - } - }, - "description": "" + "description": "Keys uninstalled." }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Bookings or machines module disabled." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Core module, or required by an installed module." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "Active superadmin access is required." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." + "description": "Makerspace not found, archived, not active, or hidden from superadmins by its own manager." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/event-categories/": { + "/api/v1/admin/makerspace/{makerspace_id}/notification-destinations": { "get": { - "operationId": "api_v1_admin_makerspaces_event_categories_retrieve", + "operationId": "api_v1_admin_makerspace_notification_destinations_list", + "summary": "List or create chat notification destinations", "parameters": [ { "in": "path", @@ -31101,17 +30824,10 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } } ], "tags": [ - "Admin events" + "Makerspaces" ], "security": [ { @@ -31123,97 +30839,29 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CategoryList" + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationDestination" + } } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Invalid field values, disabled events module or disabled feature." - }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } }, "post": { - "operationId": "api_v1_admin_makerspaces_event_categories_create", + "operationId": "api_v1_admin_makerspace_notification_destinations_create", + "summary": "List or create chat notification destinations", "parameters": [ { "in": "path", @@ -31225,23 +30873,23 @@ } ], "tags": [ - "Admin events" + "Makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CategoryInput" + "$ref": "#/components/schemas/NotificationDestinationWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CategoryInput" + "$ref": "#/components/schemas/NotificationDestinationWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CategoryInput" + "$ref": "#/components/schemas/NotificationDestinationWrite" } } }, @@ -31257,100 +30905,239 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Category" + "$ref": "#/components/schemas/NotificationDestination" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } + "description": "Invalid destination." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/notification-destinations/{destination_id}": { + "put": { + "operationId": "api_v1_admin_makerspace_notification_destinations_update", + "summary": "Update or delete a chat notification destination", + "parameters": [ + { + "in": "path", + "name": "destination_id", + "schema": { + "type": "integer" }, - "description": "Invalid field values, disabled events module or disabled feature." + "required": true }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Makerspaces" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationDestinationWrite" } }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/NotificationDestinationWrite" } }, - "description": "" + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/NotificationDestinationWrite" + } + } }, - "404": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/NotificationDestination" } } }, "description": "" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + "400": { + "description": "Invalid destination." + } + } + }, + "delete": { + "operationId": "api_v1_admin_makerspace_notification_destinations_destroy", + "summary": "Update or delete a chat notification destination", + "parameters": [ + { + "in": "path", + "name": "destination_id", + "schema": { + "type": "integer" }, - "description": "" + "required": true }, - "423": { + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Makerspaces" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "Destination removed." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/notification-recipient-rules": { + "get": { + "operationId": "api_v1_admin_makerspace_notification_recipient_rules_retrieve", + "summary": "Read or replace notification recipients", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Makerspaces" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/RecipientRulesResponse" } } }, "description": "" }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + }, + "put": { + "operationId": "api_v1_admin_makerspace_notification_recipient_rules_update", + "summary": "Read or replace notification recipients", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Makerspaces" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RecipientRulesPut" } }, - "description": "" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/RecipientRulesPut" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RecipientRulesPut" + } + } }, - "503": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/RecipientRulesResponse" } } }, "description": "" + }, + "400": { + "description": "Invalid recipient rule." + }, + "403": { + "description": "Recipient-rule permission required." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/event-collaborations/": { + "/api/v1/admin/makerspace/{makerspace_id}/notification-recipients": { "get": { - "operationId": "api_v1_admin_makerspaces_event_collaborations_list", - "summary": "List collaboration invitations for a makerspace", + "operationId": "api_v1_admin_makerspace_notification_recipients_list", + "description": "Space-manager control over which of the makerspace's managers receive the staff\nlifecycle emails. Toggling a manager off clears `receives_notifications` without\ntouching their access/role.", + "summary": "List or toggle staff email notification recipients", "parameters": [ { "in": "path", @@ -31362,7 +31149,7 @@ } ], "tags": [ - "Admin events" + "Makerspaces" ], "security": [ { @@ -31376,50 +31163,28 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/EventCollaborationInbox" + "$ref": "#/components/schemas/NotificationRecipient" } } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid collaboration request." + "401": { + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Event management access denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Event collaboration not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/event-series/": { - "get": { - "operationId": "api_v1_admin_makerspaces_event_series_retrieve", - "summary": "List recurring event series", + }, + "patch": { + "operationId": "api_v1_admin_makerspace_notification_recipients_partial_update", + "description": "Space-manager control over which of the makerspace's managers receive the staff\nlifecycle emails. Toggling a manager off clears `receives_notifications` without\ntouching their access/role.", + "summary": "List or toggle staff email notification recipients", "parameters": [ { "in": "path", @@ -31431,8 +31196,27 @@ } ], "tags": [ - "Admin event series" + "Makerspaces" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedNotificationRecipientsPatch" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedNotificationRecipientsPatch" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedNotificationRecipientsPatch" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -31443,77 +31227,77 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesListResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationRecipient" + } } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid recurring event series." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Event management access denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/notification-rules": { + "get": { + "operationId": "api_v1_admin_makerspace_notification_rules_retrieve", + "summary": "List or update makerspace notification rules", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, - "description": "Event series not found." - }, - "409": { + "required": true + } + ], + "tags": [ + "Makerspaces" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/NotificationRulesResponse" } } }, - "description": "Series state conflict." + "description": "" }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } }, - "post": { - "operationId": "api_v1_admin_makerspaces_event_series_create", - "summary": "Create a recurring event series", + "patch": { + "operationId": "api_v1_admin_makerspace_notification_rules_partial_update", + "summary": "List or update makerspace notification rules", "parameters": [ { "in": "path", @@ -31525,27 +31309,26 @@ } ], "tags": [ - "Admin event series" + "Makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesWrite" + "$ref": "#/components/schemas/PatchedNotificationRulesPatch" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/EventSeriesWrite" + "$ref": "#/components/schemas/PatchedNotificationRulesPatch" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/EventSeriesWrite" + "$ref": "#/components/schemas/PatchedNotificationRulesPatch" } } - }, - "required": true + } }, "security": [ { @@ -31553,37 +31336,66 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventSeriesMutationResponse" + "$ref": "#/components/schemas/NotificationRulesResponse" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + "description": "Invalid notification rule or preference change." + }, + "404": { + "description": "Makerspace not found." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/payment-documents": { + "get": { + "operationId": "api_v1_admin_makerspace_payment_documents_retrieve", + "summary": "List scoped payment documents", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, - "description": "Invalid recurring event series." + "required": true }, - "401": { + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Payments" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DocumentPage" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "400": { "content": { "application/json": { "schema": { @@ -31591,9 +31403,9 @@ } } }, - "description": "Event management access denied." + "description": "" }, - "404": { + "401": { "content": { "application/json": { "schema": { @@ -31601,9 +31413,9 @@ } } }, - "description": "Event series not found." + "description": "" }, - "409": { + "403": { "content": { "application/json": { "schema": { @@ -31611,9 +31423,9 @@ } } }, - "description": "Series state conflict." + "description": "" }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -31621,15 +31433,24 @@ } } }, - "description": "Rate limit exceeded." + "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/event-series-collaborations/": { + "/api/v1/admin/makerspace/{makerspace_id}/payment-documents/{document_id}/download-url": { "get": { - "operationId": "api_v1_admin_makerspaces_event_series_collaborations_list", + "operationId": "api_v1_admin_makerspace_payment_documents_download_url_retrieve", + "summary": "Get a user-bound five-minute PDF download URL", "parameters": [ + { + "in": "path", + "name": "document_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -31640,7 +31461,7 @@ } ], "tags": [ - "Admin event series" + "Payments" ], "security": [ { @@ -31652,10 +31473,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SeriesCollaborationInbox" - } + "$ref": "#/components/schemas/DownloadGrant" } } }, @@ -31669,7 +31487,7 @@ } } }, - "description": "Invalid collaboration request." + "description": "" }, "401": { "content": { @@ -31679,7 +31497,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -31689,7 +31507,7 @@ } } }, - "description": "Event management access denied." + "description": "" }, "404": { "content": { @@ -31699,35 +31517,15 @@ } } }, - "description": "Series collaboration not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Collaboration state conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/events/": { + "/api/v1/admin/makerspace/{makerspace_id}/payment-settings": { "get": { - "operationId": "api_v1_admin_makerspaces_events_retrieve", - "summary": "List events in a makerspace", + "operationId": "api_v1_admin_makerspace_payment_settings_retrieve", + "summary": "Retrieve makerspace payment settings", "parameters": [ { "in": "path", @@ -31736,47 +31534,10 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "ordering", - "schema": { - "type": "string" - }, - "description": "Allowlisted sort field; prefix with '-' for descending." - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - }, - "description": "Page number, 24 rows per page." - }, - { - "in": "query", - "name": "q", - "schema": { - "type": "string" - }, - "description": "Search within the authorized list." - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "cancelled", - "completed", - "draft", - "published" - ] - } } ], "tags": [ - "Admin events" + "Admin payment settings" ], "security": [ { @@ -31788,27 +31549,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventListWithDefaults" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/MakerspacePaymentSettings" } } }, @@ -31818,7 +31559,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentSettingsError" } } }, @@ -31828,7 +31569,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentSettingsError" } } }, @@ -31836,9 +31577,9 @@ } } }, - "post": { - "operationId": "api_v1_admin_makerspaces_events_create", - "summary": "Create a draft event", + "patch": { + "operationId": "api_v1_admin_makerspace_payment_settings_partial_update", + "summary": "Update makerspace payment settings", "parameters": [ { "in": "path", @@ -31850,27 +31591,26 @@ } ], "tags": [ - "Admin events" + "Admin payment settings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventWrite" + "$ref": "#/components/schemas/PatchedMakerspacePaymentSettings" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/EventWrite" + "$ref": "#/components/schemas/PatchedMakerspacePaymentSettings" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/EventWrite" + "$ref": "#/components/schemas/PatchedMakerspacePaymentSettings" } } - }, - "required": true + } }, "security": [ { @@ -31878,11 +31618,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventAdmin" + "$ref": "#/components/schemas/MakerspacePaymentSettings" } } }, @@ -31897,23 +31637,13 @@ } } }, - "description": "Invalid event details." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Field errors, including a credential change blocked by a live session." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentSettingsError" } } }, @@ -31923,7 +31653,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentSettingsError" } } }, @@ -31932,10 +31662,10 @@ } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/evidence-retention": { - "get": { - "operationId": "api_v1_admin_makerspaces_evidence_retention_retrieve", - "summary": "Get a makerspace evidence retention and abandoned-upload policy", + "/api/v1/admin/makerspace/{makerspace_id}/payment-settings/connect/onboard": { + "post": { + "operationId": "api_v1_admin_makerspace_payment_settings_connect_onboard_create", + "summary": "Start Stripe Connect onboarding", "parameters": [ { "in": "path", @@ -31947,7 +31677,7 @@ } ], "tags": [ - "Evidence retention" + "Admin payment settings" ], "security": [ { @@ -31959,171 +31689,70 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EvidenceRetentionPolicy" + "$ref": "#/components/schemas/StripeConnectOnboarding" } } }, "description": "" }, - "401": { - "description": "Authentication is required." - }, "403": { - "description": "Active manage-makerspace permission is required." - }, - "404": { - "description": "Makerspace was not found in the actor's scope." - }, - "503": { - "description": "Deployment recovery is active." - } - } - }, - "patch": { - "operationId": "api_v1_admin_makerspaces_evidence_retention_partial_update", - "summary": "Partially update the evidence retention and abandoned-upload policy", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Evidence retention" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedEvidenceRetentionPatch" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedEvidenceRetentionPatch" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedEvidenceRetentionPatch" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EvidenceRetentionPolicy" + "$ref": "#/components/schemas/PaymentSettingsError" } } }, "description": "" }, - "400": { - "description": "Retention days are invalid." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "Active manage-makerspace permission is required." - }, "404": { - "description": "Makerspace was not found in the actor's scope." - }, - "503": { - "description": "Deployment recovery is active." - } - } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/evidence-retention/preview": { - "post": { - "operationId": "api_v1_admin_makerspaces_evidence_retention_preview_create", - "summary": "Preview evidence object expiry and abandoned-upload cleanup", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Evidence retention" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EvidenceRetentionPreviewRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/EvidenceRetentionPreviewRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/EvidenceRetentionPreviewRequest" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EvidenceRetentionPreviewResponse" + "$ref": "#/components/schemas/PaymentSettingsError" } } }, "description": "" }, - "400": { - "description": "Preview limit is invalid." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "Active manage-makerspace permission is required." - }, - "404": { - "description": "Makerspace was not found in the actor's scope." - }, "503": { - "description": "Deployment recovery is active." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentSettingsError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/invitation-requests": { + "/api/v1/admin/makerspace/{makerspace_id}/payments": { "get": { - "operationId": "api_v1_admin_makerspaces_invitation_requests_list", - "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", - "summary": "List invitation requests (optionally filtered by ?status=)", + "operationId": "api_v1_admin_makerspace_payments_retrieve", + "description": "Requires view_finance. With for_recording=true, requires record_payment and returns only pending and offline-settled charges for counter receipt work.", + "summary": "List makerspace payments for reconciliation", "parameters": [ + { + "in": "query", + "name": "due_state", + "schema": { + "type": "string", + "enum": [ + "due_today", + "overdue", + "upcoming" + ] + } + }, + { + "in": "query", + "name": "for_recording", + "schema": { + "type": "boolean" + } + }, { "in": "path", "name": "makerspace_id", @@ -32131,10 +31760,57 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "ordering", + "schema": { + "type": "string" + }, + "description": "Allowlisted sort field; prefix with '-' for descending." + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number, 24 rows per page." + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Admin memberships" + "Payments" ], "security": [ { @@ -32146,10 +31822,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/InvitationRequest" - } + "$ref": "#/components/schemas/PaymentListResponse" } } }, @@ -32159,7 +31832,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentFilterError" } } }, @@ -32173,7 +31846,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -32183,7 +31856,7 @@ } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { @@ -32193,7 +31866,7 @@ } } }, - "description": "" + "description": "Payment or makerspace not found." }, "409": { "content": { @@ -32203,22 +31876,17 @@ } } }, - "description": "" + "description": "Payment is already terminal." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/machine-materials": { - "get": { - "operationId": "api_v1_admin_makerspaces_machine_materials_list", + "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/amend-settlement": { + "post": { + "operationId": "api_v1_admin_makerspace_payments_amend_settlement_create", + "description": "Requires record_payment or the existing scoped payment-subject authority. Appends a correction; the original receipt remains immutable.", + "summary": "Correct how a settled payment was received", "parameters": [ - { - "in": "query", - "name": "machine_type_id", - "schema": { - "type": "integer" - } - }, { "in": "path", "name": "makerspace_id", @@ -32228,18 +31896,37 @@ "required": true }, { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", + "in": "path", + "name": "payment_id", "schema": { "type": "integer" - } + }, + "required": true } ], "tags": [ - "api" + "Payments" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentOffline" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PaymentOffline" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PaymentOffline" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -32250,23 +31937,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedMaterialList" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentReconciliation" } } }, "description": "" }, - "403": { + "400": { "content": { "application/json": { "schema": { @@ -32274,9 +31951,9 @@ } } }, - "description": "" + "description": "Invalid payment request." }, - "404": { + "401": { "content": { "application/json": { "schema": { @@ -32284,9 +31961,9 @@ } } }, - "description": "" + "description": "Authentication required." }, - "423": { + "403": { "content": { "application/json": { "schema": { @@ -32294,9 +31971,9 @@ } } }, - "description": "" + "description": "Permission denied." }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -32304,9 +31981,9 @@ } } }, - "description": "" + "description": "Payment or makerspace not found." }, - "503": { + "409": { "content": { "application/json": { "schema": { @@ -32314,23 +31991,16 @@ } } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" + "description": "Payment is already terminal." } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/mark-offline": { "post": { - "operationId": "api_v1_admin_makerspaces_machine_materials_create", + "operationId": "api_v1_admin_makerspace_payments_mark_offline_create", + "description": "Requires record_payment or the existing scoped payment-subject authority. Appends an audited manual settlement receipt.", + "summary": "Mark a payment paid offline", "parameters": [ { "in": "path", @@ -32339,26 +32009,34 @@ "type": "integer" }, "required": true + }, + { + "in": "path", + "name": "payment_id", + "schema": { + "type": "integer" + }, + "required": true } ], "tags": [ - "api" + "Payments" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Material" + "$ref": "#/components/schemas/PaymentOffline" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Material" + "$ref": "#/components/schemas/PaymentOffline" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Material" + "$ref": "#/components/schemas/PaymentOffline" } } }, @@ -32370,27 +32048,17 @@ } ], "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Material" - } - } - }, - "description": "" - }, - "401": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentReconciliation" } } }, "description": "" }, - "403": { + "400": { "content": { "application/json": { "schema": { @@ -32398,9 +32066,9 @@ } } }, - "description": "" + "description": "Invalid payment request." }, - "404": { + "401": { "content": { "application/json": { "schema": { @@ -32408,9 +32076,9 @@ } } }, - "description": "" + "description": "Authentication required." }, - "423": { + "403": { "content": { "application/json": { "schema": { @@ -32418,9 +32086,9 @@ } } }, - "description": "" + "description": "Permission denied." }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -32428,9 +32096,9 @@ } } }, - "description": "" + "description": "Payment or makerspace not found." }, - "503": { + "409": { "content": { "application/json": { "schema": { @@ -32438,29 +32106,20 @@ } } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" + "description": "Payment is already terminal." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/machine-materials/{id}": { - "get": { - "operationId": "api_v1_admin_makerspaces_machine_materials_list_2", + "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/refund": { + "post": { + "operationId": "api_v1_admin_makerspace_payments_refund_create", + "description": "Sends money back through the provider that took it and records a Refund line on the payment. Only `paid_online` payments qualify; the sum of pending and succeeded refunds can never exceed the payment amount. The Payment row itself is immutable -- refund state lives on the Refund rows returned in `refunds`.", + "summary": "Refund a payment settled online, fully or partially", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -32468,25 +32127,36 @@ }, { "in": "path", - "name": "makerspace_id", + "name": "payment_id", "schema": { "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "api" + "Payments" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentRefundRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PaymentRefundRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PaymentRefundRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -32497,13 +32167,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedMaterialList" + "$ref": "#/components/schemas/PaymentReconciliation" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -32511,7 +32181,7 @@ } } }, - "description": "" + "description": "Refund not allowed or over the balance." }, "403": { "content": { @@ -32521,7 +32191,7 @@ } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { @@ -32531,29 +32201,9 @@ } } }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Payment or makerspace not found." }, - "503": { + "502": { "content": { "application/json": { "schema": { @@ -32561,27 +32211,20 @@ } } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" + "description": "The provider rejected the refund." } } - }, - "patch": { - "operationId": "api_v1_admin_makerspaces_machine_materials_partial_update", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/refund-offline": { + "post": { + "operationId": "api_v1_admin_makerspace_payments_refund_offline_create", + "description": "Requires active staff and record_payment. Records money already returned on a paid_offline charge, fully or partially, with method, optional financial reference and refunded_at. The receipt is append-only and audited; all pending/succeeded refunds share one balance. No provider call or payments module is required. The Payment remains unchanged.", + "summary": "Record an offline refund or release a cash loan deposit", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -32589,7 +32232,7 @@ }, { "in": "path", - "name": "makerspace_id", + "name": "payment_id", "schema": { "type": "integer" }, @@ -32597,26 +32240,27 @@ } ], "tags": [ - "api" + "Payments" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedMaterial" + "$ref": "#/components/schemas/PaymentOfflineRefund" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedMaterial" + "$ref": "#/components/schemas/PaymentOfflineRefund" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedMaterial" + "$ref": "#/components/schemas/PaymentOfflineRefund" } } - } + }, + "required": true }, "security": [ { @@ -32628,43 +32272,23 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Material" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentReconciliation" } } }, "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ManualRefundError" } } }, - "description": "" + "description": "Invalid receipt, status or refund balance." }, - "423": { + "401": { "content": { "application/json": { "schema": { @@ -32672,9 +32296,9 @@ } } }, - "description": "" + "description": "Authentication required." }, - "429": { + "403": { "content": { "application/json": { "schema": { @@ -32682,9 +32306,9 @@ } } }, - "description": "" + "description": "Record payment and active status required." }, - "503": { + "404": { "content": { "application/json": { "schema": { @@ -32692,27 +32316,20 @@ } } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" + "description": "Payment not found in the permitted makerspace." } } - }, - "delete": { - "operationId": "api_v1_admin_makerspaces_machine_materials_destroy", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/refunds/{refund_id}/amend": { + "post": { + "operationId": "api_v1_admin_makerspace_payments_refunds_amend_create", + "description": "Requires record_payment. Replaces method, reference and returned date on the effective receipt; amount/currency and the original payment never change. No second refund event or transfer occurs.", + "summary": "Append a corrected manual refund receipt", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -32720,7 +32337,15 @@ }, { "in": "path", - "name": "makerspace_id", + "name": "payment_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "refund_id", "schema": { "type": "integer" }, @@ -32728,48 +32353,55 @@ } ], "tags": [ - "api" + "Payments" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentRefundAmend" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PaymentRefundAmend" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PaymentRefundAmend" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "204": { - "description": "No response body" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaymentReconciliation" } } }, "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ManualRefundError" } } }, "description": "" }, - "423": { + "401": { "content": { "application/json": { "schema": { @@ -32779,7 +32411,7 @@ }, "description": "" }, - "429": { + "403": { "content": { "application/json": { "schema": { @@ -32789,7 +32421,7 @@ }, "description": "" }, - "503": { + "404": { "content": { "application/json": { "schema": { @@ -32798,24 +32430,15 @@ } }, "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/machine-service/consumable-pools": { - "get": { - "operationId": "api_v1_admin_makerspaces_machine_service_consumable_pools_list", + "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/release-deposit": { + "post": { + "operationId": "api_v1_admin_makerspace_payments_release_deposit_create", + "description": "Requires record_payment. Only a paid_offline loan deposit on a returned or closed_with_issue loan is eligible. Records actual money returned; never offsets fees or calls the rail.", + "summary": "Record a deposit returned after a loan closes", "parameters": [ { "in": "path", @@ -32824,38 +32447,10 @@ "type": "integer" }, "required": true - } - ], - "tags": [ - "Admin machine service" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PrinterPool" - } - } - } - }, - "description": "" - } - } - }, - "post": { - "operationId": "api_v1_admin_makerspaces_machine_service_consumable_pools_create", - "parameters": [ + }, { "in": "path", - "name": "makerspace_id", + "name": "payment_id", "schema": { "type": "integer" }, @@ -32863,23 +32458,23 @@ } ], "tags": [ - "Admin machine service" + "Payments" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrinterPoolCreate" + "$ref": "#/components/schemas/PaymentOfflineRefund" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PrinterPoolCreate" + "$ref": "#/components/schemas/PaymentOfflineRefund" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PrinterPoolCreate" + "$ref": "#/components/schemas/PaymentOfflineRefund" } } }, @@ -32891,11 +32486,51 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrinterPool" + "$ref": "#/components/schemas/PaymentReconciliation" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ManualRefundError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -32904,39 +32539,11 @@ } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/machine-service/requests": { - "get": { - "operationId": "api_v1_admin_makerspaces_machine_service_requests_list", - "summary": "List machine service requests", + "/api/v1/admin/makerspace/{makerspace_id}/payments/{payment_id}/waive": { + "post": { + "operationId": "api_v1_admin_makerspace_payments_waive_create", + "summary": "Waive a payment", "parameters": [ - { - "in": "query", - "name": "bucket", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "machine", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "machine_type", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "machine_type_id", - "schema": { - "type": "integer" - } - }, { "in": "path", "name": "makerspace_id", @@ -32946,22 +32553,16 @@ "required": true }, { - "in": "query", - "name": "queue", + "in": "path", + "name": "payment_id", "schema": { "type": "integer" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string" - } + }, + "required": true } ], "tags": [ - "Admin machine service" + "Payments" ], "security": [ { @@ -32973,10 +32574,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineServiceRequest" - } + "$ref": "#/components/schemas/PaymentReconciliation" } } }, @@ -32990,16 +32588,37 @@ } } }, - "description": "Invalid service request input." + "description": "Invalid payment request." }, "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, "description": "Authentication required." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Service request was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Payment or makerspace not found." }, "409": { "content": { @@ -33009,13 +32628,16 @@ } } }, - "description": "Service workflow conflict." + "description": "Payment is already terminal." } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/payments/bulk/mark-offline": { "post": { - "operationId": "api_v1_admin_makerspaces_machine_service_requests_create", - "summary": "Submit a machine service request for a member", + "operationId": "api_v1_admin_makerspace_payments_bulk_mark_offline_create", + "description": "Requires record_payment or the existing scoped payment-subject authority for every charge in the batch.", + "summary": "Mark payments paid offline in one transaction", "parameters": [ { "in": "path", @@ -33027,23 +32649,23 @@ } ], "tags": [ - "Admin machine service" + "Payments" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceSubmit" + "$ref": "#/components/schemas/PaymentBulkOffline" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MachineServiceSubmit" + "$ref": "#/components/schemas/PaymentBulkOffline" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MachineServiceSubmit" + "$ref": "#/components/schemas/PaymentBulkOffline" } } }, @@ -33055,11 +32677,14 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MachineServiceRequest" + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentReconciliation" + } } } }, @@ -33073,16 +32698,37 @@ } } }, - "description": "Invalid service request input." + "description": "Invalid payment request." }, "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, "description": "Authentication required." }, "403": { - "description": "Machine management permission required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Service request was not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Payment or makerspace not found." }, "409": { "content": { @@ -33092,29 +32738,16 @@ } } }, - "description": "Service workflow conflict." + "description": "Payment is already terminal." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/machine-service/typed-manual-usage": { - "get": { - "operationId": "api_v1_admin_makerspaces_machine_service_typed_manual_usage_list", + "/api/v1/admin/makerspace/{makerspace_id}/payments/bulk/waive": { + "post": { + "operationId": "api_v1_admin_makerspace_payments_bulk_waive_create", + "summary": "Waive payments in one transaction", "parameters": [ - { - "in": "query", - "name": "machine_type", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "machine_type_id", - "schema": { - "type": "integer" - } - }, { "in": "path", "name": "makerspace_id", @@ -33125,8 +32758,28 @@ } ], "tags": [ - "Admin machine service" + "Payments" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentBulkAction" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PaymentBulkAction" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PaymentBulkAction" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -33139,18 +32792,82 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/TypedManualUsageResponse" + "$ref": "#/components/schemas/PaymentReconciliation" } } } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid payment request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Payment or makerspace not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Payment is already terminal." } } - }, - "post": { - "operationId": "api_v1_admin_makerspaces_machine_service_typed_manual_usage_create", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/pending-requests": { + "get": { + "operationId": "api_v1_admin_makerspace_pending_requests_list", + "summary": "List pending borrow requests", "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "description": "Select one result within the authorized list.", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 9223372036854775807 + } + }, { "in": "path", "name": "makerspace_id", @@ -33158,53 +32875,168 @@ "type": "integer" }, "required": true + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "required": false, + "in": "query", + "description": "A search term (requested-for, requester name/email).", + "schema": { + "type": "string" + } } ], "tags": [ - "Admin machine service" + "Admin requests" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedManualUsage" + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedAdminRequestList" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/TypedManualUsage" + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/TypedManualUsage" + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "Authentication required." }, - "required": true - }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/presence-sessions/current": { + "get": { + "operationId": "api_v1_admin_makerspace_presence_sessions_current_list", + "description": "Requires manage_members in this makerspace.", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin makerspaces" + ], "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedManualUsageResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/PresenceRoster" + } } } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Membership permission required." + }, + "404": { + "description": "Makerspace not found." + }, + "429": { + "description": "Rate limit exceeded." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/machine-types/{type_id}/intake": { + "/api/v1/admin/makerspace/{makerspace_id}/printer-jobs": { "get": { - "operationId": "api_v1_admin_makerspaces_machine_types_intake_retrieve", + "operationId": "api_v1_admin_makerspace_printer_jobs_list", "parameters": [ { "in": "path", @@ -33215,16 +33047,17 @@ "required": true }, { - "in": "path", - "name": "type_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "api" + "Devices" ], "security": [ { @@ -33236,87 +33069,86 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Intake" + "$ref": "#/components/schemas/PaginatedJobList" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Invalid payload or adapter module disabled." }, - "403": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Authentication/signature/replay refused." }, - "404": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Authority or device revoked." }, - "423": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Target outside scope or absent." }, - "429": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Conflicting command/report." }, - "503": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Delivery lease expired." }, - "400": { + "423": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Tenant frozen for migration." } } }, - "put": { - "operationId": "api_v1_admin_makerspaces_machine_types_intake_update", + "post": { + "operationId": "api_v1_admin_makerspace_printer_jobs_create", "parameters": [ { "in": "path", @@ -33325,34 +33157,26 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "type_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "api" + "Devices" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Intake" + "$ref": "#/components/schemas/JobCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Intake" + "$ref": "#/components/schemas/JobCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Intake" + "$ref": "#/components/schemas/JobCreate" } } }, @@ -33364,98 +33188,97 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Intake" + "$ref": "#/components/schemas/Job" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Invalid payload or adapter module disabled." }, - "403": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Authentication/signature/replay refused." }, - "404": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Authority or device revoked." }, - "423": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Target outside scope or absent." }, - "429": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Conflicting command/report." }, - "503": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Delivery lease expired." }, - "400": { + "423": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Tenant frozen for migration." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/machines/{machine_id}/maintenance/logs/": { - "get": { - "operationId": "api_v1_admin_makerspaces_machines_maintenance_logs_retrieve", - "summary": "List immutable machine maintenance logs", + "/api/v1/admin/makerspace/{makerspace_id}/problem-reports/{id}/resolve": { + "post": { + "operationId": "api_v1_admin_makerspace_problem_reports_resolve_create", + "summary": "Resolve a public problem report", "parameters": [ { "in": "path", - "name": "machine_id", + "name": "id", "schema": { "type": "integer" }, @@ -33471,7 +33294,7 @@ } ], "tags": [ - "Admin maintenance" + "Analytics" ], "security": [ { @@ -33483,34 +33306,24 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceLogList" + "type": "object", + "additionalProperties": {} } } }, "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/problem-reports/{id}/triage": { "post": { - "operationId": "api_v1_admin_makerspaces_machines_maintenance_logs_create", - "description": "When schedule_id is supplied, complete that schedule and set its next due date according to its recurrence anchor. Completion anchors add interval_days to the makerspace-local date of performed_at; calendar anchors advance the previous due date by whole intervals past completion. performed_at defaults to now; backdated entries use their recorded completion date.", - "summary": "Record completed machine maintenance", + "operationId": "api_v1_admin_makerspace_problem_reports_triage_create", + "summary": "Triage a public problem report", "parameters": [ { "in": "path", - "name": "machine_id", + "name": "id", "schema": { "type": "integer" }, @@ -33526,23 +33339,23 @@ } ], "tags": [ - "Admin maintenance" + "Analytics" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceLogWrite" + "$ref": "#/components/schemas/ProblemReportTriage" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MaintenanceLogWrite" + "$ref": "#/components/schemas/ProblemReportTriage" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MaintenanceLogWrite" + "$ref": "#/components/schemas/ProblemReportTriage" } } }, @@ -33554,50 +33367,25 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceLog" + "$ref": "#/components/schemas/ProblemReportTriageResponse" } } }, "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "409": { - "description": "Conflicts with the current state." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/machines/{machine_id}/maintenance/schedules/": { + "/api/v1/admin/makerspace/{makerspace_id}/profile": { "get": { - "operationId": "api_v1_admin_makerspaces_machines_maintenance_schedules_retrieve", - "summary": "List machine maintenance schedules", + "operationId": "api_v1_admin_makerspace_profile_retrieve", + "description": "Core configuration: usable even with every optional module switched off.", + "summary": "Read location and opening hours", "parameters": [ - { - "in": "path", - "name": "machine_id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -33608,7 +33396,7 @@ } ], "tags": [ - "Admin maintenance" + "Admin makerspaces" ], "security": [ { @@ -33620,15 +33408,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceScheduleList" + "$ref": "#/components/schemas/MakerspaceProfile" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, "401": { "description": "Not signed in." }, @@ -33640,18 +33425,11 @@ } } }, - "post": { - "operationId": "api_v1_admin_makerspaces_machines_maintenance_schedules_create", - "summary": "Create a machine maintenance schedule", + "patch": { + "operationId": "api_v1_admin_makerspace_profile_partial_update", + "description": "Requires manage_makerspace. Empty hours mean always open. Dated exceptions replace weekly hours. Currency updates the existing payment default, never currencies on existing charges.", + "summary": "Update location and opening hours", "parameters": [ - { - "in": "path", - "name": "machine_id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -33662,27 +33440,26 @@ } ], "tags": [ - "Admin maintenance" + "Admin makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceScheduleWrite" + "$ref": "#/components/schemas/PatchedMakerspaceProfile" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MaintenanceScheduleWrite" + "$ref": "#/components/schemas/PatchedMakerspaceProfile" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MaintenanceScheduleWrite" + "$ref": "#/components/schemas/PatchedMakerspaceProfile" } } - }, - "required": true + } }, "security": [ { @@ -33690,40 +33467,62 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MaintenanceSchedule" + "$ref": "#/components/schemas/MakerspaceProfile" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "409": { - "description": "Conflicts with the current state." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileError" + } + } + }, + "description": "" }, "423": { - "description": "Makerspace writes are frozen for tenant migration." - } - } + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileGateError" + } + } + }, + "description": "Tenant writes are frozen for migration." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileGateError" + } + } + }, + "description": "The source gate cannot prove writes are safe." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/maintenance/feed": { - "get": { - "operationId": "api_v1_admin_makerspaces_maintenance_feed_retrieve", + "/api/v1/admin/makerspace/{makerspace_id}/provision-subdomain": { + "post": { + "operationId": "api_v1_admin_makerspace_provision_subdomain_create", + "summary": "Provision a platform subdomain for a makerspace", "parameters": [ { "in": "path", @@ -33735,8 +33534,28 @@ } ], "tags": [ - "api" + "Admin makerspaces" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProvisionSubdomainRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ProvisionSubdomainRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ProvisionSubdomainRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -33747,88 +33566,182 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedState" + "$ref": "#/components/schemas/Makerspace" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ProvisionSubdomainValidationError" } } }, - "description": "" + "description": "Invalid or unavailable platform subdomain label." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/HostingError" } } }, - "description": "" + "description": "Active superadmin access is required." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/HostingError" } } }, - "description": "" + "description": "Makerspace not found." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/qr-print-batches": { + "get": { + "operationId": "api_v1_admin_makerspace_qr_print_batches_list", + "summary": "List QR print batches", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true }, - "423": { + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "QR print batches" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaginatedQrPrintBatchList" } } }, "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + } + } + }, + "post": { + "operationId": "api_v1_admin_makerspace_qr_print_batches_create", + "summary": "Create QR print batch", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "QR print batches" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QrPrintBatchCreate" } }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/QrPrintBatchCreate" } }, - "description": "" + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/QrPrintBatchCreate" + } + } }, - "400": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/QrPrintBatch" } } }, "description": "" } } - }, - "post": { - "operationId": "api_v1_admin_makerspaces_maintenance_feed_create", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/reports/{report_key}/export": { + "get": { + "operationId": "api_v1_admin_makerspace_reports_export_retrieve", + "description": "Subject reports require live resource management plus tenant VIEW_AUDIT. For bookable_space use booking-resource-utilization, booking-reservations or booking-answers; bookings.analytics / bookings.exports must be enabled. Resource PDF requires tenant MANAGE_BOOKINGS, bookings.utilization_pdf and complete start/end dates. Dates are tenant-local, end inclusive; reservation detail includes overlapping windows. No credentials are exported. Event PDFs require full event management and an ended event. Subjects cannot be scheduled or aggregated. Active-loan and return detail rows include count=1 for chart summaries of returned rows. Event, booking and audit detail exports retain their explicit column allowlists and use table specs. FabLab health rows include makerspace_id in tenant previews.", + "summary": "Export report", "parameters": [ + { + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "format", + "schema": { + "type": "string", + "enum": [ + "csv", + "pdf", + "xlsx" + ] + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, { "in": "path", "name": "makerspace_id", @@ -33836,10 +33749,167 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" + }, + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." + }, + { + "in": "path", + "name": "report_key", + "schema": { + "type": "string", + "enum": [ + "accounting-journal", + "active-loans", + "asset-usage", + "audit-activity", + "audit-activity-hours", + "audit-top-actors", + "audit-trail", + "booking-answers", + "booking-lead-time", + "booking-occupancy", + "booking-reservations", + "booking-resource-utilization", + "booking-resource-utilization-pdf", + "booking-top-members", + "booking-utilization", + "cash-book", + "certification-coverage", + "communications-health", + "community-engagement", + "consumable-pools", + "consumable-usage", + "damaged-lost", + "damaged-missing", + "device-health", + "event-attendance", + "event-attendance-pdf", + "event-checkin-times", + "event-feedback", + "event-feedback-scores", + "event-funnel", + "event-registrations", + "event-revenue", + "event-signups", + "evidence-compliance", + "fablab-health", + "import-quality", + "inventory-control", + "inventory-utilisation", + "inventory-watch-demand", + "loan-throughput", + "longest-loans", + "machine-downtime", + "machine-errors", + "machine-service", + "machine-usage", + "machine-usage-heatmap", + "machine-usage-members", + "maintenance-activity", + "member-activity", + "member-retention", + "membership-growth", + "membership-revenue", + "mobile-auth", + "mobile-devices", + "module-operational-health", + "most-lent", + "organization-membership", + "overdue-loans", + "payment-cash-book", + "payment-reconciliation", + "payments-ageing", + "payments-ledger", + "platform-services", + "platform-usage", + "presence-activity", + "presence-activity-hours", + "presence-top-visitors", + "printer-jobs", + "printer-service", + "problem-reports", + "procurement-performance", + "qr-scan-actors", + "qr-scans", + "recently-added", + "request-funnel", + "returns", + "revenue-over-time", + "service-queue-times", + "stock-health", + "stock-transfers", + "stocktake-variance", + "summary", + "taken-items", + "top-borrowers", + "warranty-expiry" + ] + }, + "required": true + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_id", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "subject_kind", + "schema": { + "type": "string" + }, + "description": "Typed tenant-local subject (event or bookable_space), paired with subject_id. Booking resources require manage_reservations plus tenant VIEW_AUDIT; resource PDF requires tenant MANAGE_BOOKINGS and both dates. Subject filters cannot be scheduled or aggregated." + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "api" + "Reports" ], "security": [ { @@ -33847,91 +33917,85 @@ } ], "responses": { - "200": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedIssued" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Render rate limit exceeded." }, - "401": { + "200": { "content": { - "application/json": { + "application/pdf": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "string", + "format": "binary" } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { + }, + "text/csv": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "string" } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "string", + "format": "binary" } } }, "description": "" }, - "423": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, - "429": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, - "503": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, - "400": { + "404": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or report not found." } } - }, - "delete": { - "operationId": "api_v1_admin_makerspaces_maintenance_feed_destroy", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/reports/catalog": { + "get": { + "operationId": "api_v1_admin_makerspace_reports_catalog_retrieve", + "summary": "List makerspace report catalog", "parameters": [ { "in": "path", @@ -33943,7 +34007,7 @@ } ], "tags": [ - "api" + "Reports" ], "security": [ { @@ -33951,87 +34015,75 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportCatalog" } } }, "description": "" }, - "423": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid report request." }, - "429": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, - "503": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, - "400": { + "404": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/maintenance/feed/{raw_token}.ics": { + "/api/v1/admin/makerspace/{makerspace_id}/request-history": { "get": { - "operationId": "api_v1_admin_makerspaces_maintenance_feed_.ics_retrieve", + "operationId": "api_v1_admin_makerspace_request_history_list", + "summary": "List terminal request history (returned / rejected / closed with issue)", "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "description": "Select one result within the authorized list.", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 9223372036854775807 + } + }, { "in": "path", "name": "makerspace_id", @@ -34041,33 +34093,53 @@ "required": true }, { - "in": "path", - "name": "raw_token", + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", "schema": { "type": "string" - }, - "required": true + } + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "required": false, + "in": "query", + "description": "A search term (requested-for, requester name/email).", + "schema": { + "type": "string" + } } ], "tags": [ - "api" + "Admin requests" + ], + "security": [ + { + "jwtAuth": [] + } ], "responses": { "200": { "content": { - "text/calendar": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/PaginatedAdminRequestList" } } }, "description": "" }, - "304": { - "description": "No response body" - }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -34075,9 +34147,9 @@ } } }, - "description": "" + "description": "Invalid request." }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -34085,9 +34157,9 @@ } } }, - "description": "" + "description": "Authentication required." }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -34095,29 +34167,9 @@ } } }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Permission denied." }, - "503": { + "404": { "content": { "application/json": { "schema": { @@ -34125,26 +34177,14 @@ } } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - }, - "description": "" + "description": "Not found." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/member-card-template": { + "/api/v1/admin/makerspace/{makerspace_id}/return-policy": { "get": { - "operationId": "api_v1_admin_makerspaces_member_card_template_retrieve", - "summary": "Read the card layout template", + "operationId": "api_v1_admin_makerspace_return_policy_retrieve", "parameters": [ { "in": "path", @@ -34156,7 +34196,7 @@ } ], "tags": [ - "Admin memberships" + "Admin makerspaces" ], "security": [ { @@ -34168,7 +34208,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardTemplate" + "$ref": "#/components/schemas/ReturnPolicy" } } }, @@ -34182,7 +34222,7 @@ } } }, - "description": "Invalid card input or membership module disabled." + "description": "" }, "401": { "content": { @@ -34192,7 +34232,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -34202,7 +34242,7 @@ } } }, - "description": "Missing member-card action or inactive account." + "description": "" }, "404": { "content": { @@ -34212,13 +34252,32 @@ } } }, - "description": "Not found in the permitted makerspace." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } }, - "put": { - "operationId": "api_v1_admin_makerspaces_member_card_template_update", - "summary": "Replace the card layout template", + "patch": { + "operationId": "api_v1_admin_makerspace_return_policy_partial_update", "parameters": [ { "in": "path", @@ -34230,23 +34289,23 @@ } ], "tags": [ - "Admin memberships" + "Admin makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardTemplate" + "$ref": "#/components/schemas/PatchedReturnPolicy" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberCardTemplate" + "$ref": "#/components/schemas/PatchedReturnPolicy" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberCardTemplate" + "$ref": "#/components/schemas/PatchedReturnPolicy" } } } @@ -34261,7 +34320,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardTemplate" + "$ref": "#/components/schemas/ReturnPolicy" } } }, @@ -34275,7 +34334,7 @@ } } }, - "description": "Invalid card input or membership module disabled." + "description": "" }, "401": { "content": { @@ -34285,7 +34344,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -34295,7 +34354,7 @@ } } }, - "description": "Missing member-card action or inactive account." + "description": "" }, "404": { "content": { @@ -34305,15 +34364,35 @@ } } }, - "description": "Not found in the permitted makerspace." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/member-cards": { + "/api/v1/admin/makerspace/{makerspace_id}/setup-state": { "get": { - "operationId": "api_v1_admin_makerspaces_member_cards_list", - "summary": "List member cards", + "operationId": "api_v1_admin_makerspace_setup_state_retrieve", + "summary": "Read derived makerspace setup progress", "parameters": [ { "in": "path", @@ -34322,18 +34401,55 @@ "type": "integer" }, "required": true + } + ], + "tags": [ + "Makerspaces" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetupState" + } + } + }, + "description": "" + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/space-pack": { + "get": { + "operationId": "api_v1_admin_makerspace_space_pack_retrieve", + "summary": "Export configuration-only space pack", + "parameters": [ { - "in": "query", - "name": "status", + "in": "path", + "name": "makerspace_id", "schema": { - "type": "string" + "type": "integer" }, - "description": "active | revoked" + "required": true } ], "tags": [ - "Admin memberships" + "Admin makerspaces" ], "security": [ { @@ -34345,10 +34461,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberCard" - } + "$ref": "#/components/schemas/SpacePack" } } }, @@ -34358,49 +34471,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid card input or membership module disabled." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Missing member-card action or inactive account." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Not found in the permitted makerspace." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" } } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/member-cards.pdf": { + }, "post": { - "operationId": "api_v1_admin_makerspaces_member_cards.pdf_create", - "summary": "Print a sheet of cards", + "operationId": "api_v1_admin_makerspace_space_pack_create", + "description": "Dry-run defaults true. Apply needs base_checksum from preview. Modules remain superadmin-only. Unrelated entries are retained.", + "summary": "Preview or atomically apply a space pack", "parameters": [ { "in": "path", @@ -34412,26 +34554,27 @@ } ], "tags": [ - "Admin memberships" + "Admin makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardPrint" + "$ref": "#/components/schemas/PackImport" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberCardPrint" + "$ref": "#/components/schemas/PackImport" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberCardPrint" + "$ref": "#/components/schemas/PackImport" } } - } + }, + "required": true }, "security": [ { @@ -34441,10 +34584,9 @@ "responses": { "200": { "content": { - "application/pdf": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/PackPreview" } } }, @@ -34454,49 +34596,79 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid card input or membership module disabled." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Missing member-card action or inactive account." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Not found in the permitted makerspace." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/member-cards/{membership_id}/issue": { - "post": { - "operationId": "api_v1_admin_makerspaces_member_cards_issue_create", - "summary": "Issue a card to a membership", + "/api/v1/admin/makerspace/{makerspace_id}/stock-transfers": { + "get": { + "operationId": "api_v1_admin_makerspace_stock_transfers_list", + "summary": "List stock transfers", "parameters": [ { "in": "path", @@ -34507,47 +34679,29 @@ "required": true }, { - "in": "path", - "name": "membership_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Admin memberships" + "Stock transfers" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MemberCardIssue" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MemberCardIssue" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MemberCardIssue" - } - } - } - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCard" + "$ref": "#/components/schemas/PaginatedStockTransferList" } } }, @@ -34557,49 +34711,26 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "Invalid card input or membership module disabled." + "description": "Invalid request." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Missing member-card action or inactive account." + "description": "Permission denied." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found in the permitted makerspace." + "description": "Not found." } } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/member-cards/resolve": { + }, "post": { - "operationId": "api_v1_admin_makerspaces_member_cards_resolve_create", - "summary": "Resolve a scanned member card to minimal identity", + "operationId": "api_v1_admin_makerspace_stock_transfers_create", + "summary": "Create stock transfer", "parameters": [ { "in": "path", @@ -34611,23 +34742,23 @@ } ], "tags": [ - "Admin memberships" + "Stock transfers" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardResolve" + "$ref": "#/components/schemas/StockTransferCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberCardResolve" + "$ref": "#/components/schemas/StockTransferCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberCardResolve" + "$ref": "#/components/schemas/StockTransferCreate" } } }, @@ -34639,11 +34770,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardResolveResult" + "$ref": "#/components/schemas/StockTransfer" } } }, @@ -34653,49 +34784,27 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "Invalid card input or membership module disabled." + "description": "Invalid request." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Missing member-card action or inactive account." + "description": "Permission denied." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found in the permitted makerspace." + "description": "Not found." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/member-claim-codes": { + "/api/v1/admin/makerspace/{makerspace_id}/stocktake-policy": { "get": { - "operationId": "api_v1_admin_makerspaces_member_claim_codes_list", - "summary": "List active physically handed member claim codes", + "operationId": "api_v1_admin_makerspace_stocktake_policy_retrieve", "parameters": [ { "in": "path", @@ -34707,7 +34816,7 @@ } ], "tags": [ - "Admin memberships" + "api" ], "security": [ { @@ -34719,16 +34828,13 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberClaimCode" - } + "$ref": "#/components/schemas/Policy" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -34738,7 +34844,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -34748,7 +34854,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -34758,7 +34864,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -34768,7 +34874,17 @@ }, "description": "" }, - "409": { + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { "content": { "application/json": { "schema": { @@ -34777,12 +34893,22 @@ } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" } } }, - "post": { - "operationId": "api_v1_admin_makerspaces_member_claim_codes_create", - "summary": "Issue a claim code to an eligible walk-in member", + "put": { + "operationId": "api_v1_admin_makerspace_stocktake_policy_update", "parameters": [ { "in": "path", @@ -34794,27 +34920,26 @@ } ], "tags": [ - "Admin memberships" + "api" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberClaimCodeIssueRequest" + "$ref": "#/components/schemas/Policy" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberClaimCodeIssueRequest" + "$ref": "#/components/schemas/Policy" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberClaimCodeIssueRequest" + "$ref": "#/components/schemas/Policy" } } - }, - "required": true + } }, "security": [ { @@ -34822,17 +34947,17 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberClaimCodeIssueResponse" + "$ref": "#/components/schemas/Policy" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -34842,7 +34967,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -34852,7 +34977,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -34862,7 +34987,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -34872,7 +34997,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -34882,7 +35007,7 @@ }, "description": "" }, - "429": { + "503": { "content": { "application/json": { "schema": { @@ -34890,35 +35015,46 @@ } } }, - "description": "Issue rate limit exceeded." + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/member-claim-codes/{claim_id}/revoke": { - "post": { - "operationId": "api_v1_admin_makerspaces_member_claim_codes_revoke_create", - "summary": "Revoke a member claim code and its bound session", + "/api/v1/admin/makerspace/{makerspace_id}/stocktake-schedules": { + "get": { + "operationId": "api_v1_admin_makerspace_stocktake_schedules_list", "parameters": [ { "in": "path", - "name": "claim_id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "in": "path", - "name": "makerspace_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Admin memberships" + "api" ], "security": [ { @@ -34930,13 +35066,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberClaimCode" + "$ref": "#/components/schemas/PaginatedScheduleList" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -34946,7 +35082,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -34956,7 +35092,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -34966,7 +35102,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -34976,7 +35112,17 @@ }, "description": "" }, - "409": { + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { "content": { "application/json": { "schema": { @@ -34985,14 +35131,22 @@ } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" } } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/membership-admission": { - "get": { - "operationId": "api_v1_admin_makerspaces_membership_admission_retrieve", - "description": "Requires manage_makerspace; settings are separate from manage_members.", + }, + "post": { + "operationId": "api_v1_admin_makerspace_stocktake_schedules_create", "parameters": [ { "in": "path", @@ -35004,25 +35158,45 @@ } ], "tags": [ - "Admin memberships" + "api" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Schedule" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Schedule" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Schedule" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdmissionSettings" + "$ref": "#/components/schemas/Schedule" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -35032,7 +35206,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -35042,7 +35216,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -35052,7 +35226,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -35062,7 +35236,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -35072,7 +35246,7 @@ }, "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -35082,22 +35256,32 @@ }, "description": "" }, - "503": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, "description": "" } } - }, - "patch": { - "operationId": "api_v1_admin_makerspaces_membership_admission_partial_update", - "description": "Requires manage_makerspace. Paid admissions require charge tracking, not an online gateway.", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/stocktake-schedules/{id}": { + "get": { + "operationId": "api_v1_admin_makerspace_stocktake_schedules_retrieve", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -35108,27 +35292,8 @@ } ], "tags": [ - "Admin memberships" + "api" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedAdmissionSettings" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedAdmissionSettings" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedAdmissionSettings" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -35139,13 +35304,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdmissionSettings" + "$ref": "#/components/schemas/Schedule" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -35155,7 +35320,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -35165,7 +35330,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -35175,7 +35340,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -35185,7 +35350,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -35195,7 +35360,7 @@ }, "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -35205,25 +35370,30 @@ }, "description": "" }, - "503": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, "description": "" } } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/membership-plans": { - "get": { - "operationId": "api_v1_admin_makerspaces_membership_plans_list", - "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", - "summary": "List membership plans", + }, + "patch": { + "operationId": "api_v1_admin_makerspace_stocktake_schedules_partial_update", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -35234,8 +35404,27 @@ } ], "tags": [ - "Admin memberships" + "api" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedSchedule" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedSchedule" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedSchedule" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -35246,16 +35435,13 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MembershipPlan" - } + "$ref": "#/components/schemas/Schedule" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -35265,7 +35451,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -35275,7 +35461,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -35285,7 +35471,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -35295,7 +35481,17 @@ }, "description": "" }, - "409": { + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { "content": { "application/json": { "schema": { @@ -35304,14 +35500,31 @@ } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" } } }, - "post": { - "operationId": "api_v1_admin_makerspaces_membership_plans_create", - "description": "Requires manage_makerspace in this makerspace; manage_members alone cannot change plan definitions or prices.", - "summary": "Create a membership plan", + "delete": { + "operationId": "api_v1_admin_makerspace_stocktake_schedules_destroy", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -35322,45 +35535,28 @@ } ], "tags": [ - "Admin memberships" + "api" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MembershipPlan" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MembershipPlan" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MembershipPlan" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "204": { + "description": "No response body" + }, + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipPlan" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "403": { "content": { "application/json": { "schema": { @@ -35370,7 +35566,7 @@ }, "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { @@ -35380,7 +35576,7 @@ }, "description": "" }, - "403": { + "423": { "content": { "application/json": { "schema": { @@ -35390,7 +35586,7 @@ }, "description": "" }, - "404": { + "429": { "content": { "application/json": { "schema": { @@ -35400,7 +35596,7 @@ }, "description": "" }, - "409": { + "503": { "content": { "application/json": { "schema": { @@ -35409,14 +35605,25 @@ } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/memberships": { + "/api/v1/admin/makerspace/{makerspace_id}/stocktakes": { "get": { - "operationId": "api_v1_admin_makerspaces_memberships_list", - "description": "Requires manage_members in this makerspace. Staff creation and role administration require manage_makerspace.", + "operationId": "api_v1_admin_makerspace_stocktakes_list", + "summary": "List stocktakes", "parameters": [ { "in": "path", @@ -35425,10 +35632,34 @@ "type": "integer" }, "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "applied", + "approved", + "cancelled", + "completed", + "counting", + "draft" + ] + } } ], "tags": [ - "Admin memberships" + "Stocktake" ], "security": [ { @@ -35440,10 +35671,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MembershipList" - } + "$ref": "#/components/schemas/PaginatedStocktakeList" } } }, @@ -35453,56 +35681,26 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "" + "description": "Invalid request or stocktake state." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Permission denied." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found." } } }, "post": { - "operationId": "api_v1_admin_makerspaces_memberships_create", + "operationId": "api_v1_admin_makerspace_stocktakes_create", + "summary": "Create stocktake", "parameters": [ { "in": "path", @@ -35514,27 +35712,26 @@ } ], "tags": [ - "Admin memberships" + "Stocktake" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipCreate" + "$ref": "#/components/schemas/StocktakeCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MembershipCreate" + "$ref": "#/components/schemas/StocktakeCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MembershipCreate" + "$ref": "#/components/schemas/StocktakeCreate" } } - }, - "required": true + } }, "security": [ { @@ -35546,7 +35743,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipList" + "$ref": "#/components/schemas/Stocktake" } } }, @@ -35556,58 +35753,91 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "" + "description": "Invalid request or stocktake state." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication credentials were not provided." }, "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/subdomain-request": { + "get": { + "operationId": "api_v1_admin_makerspace_subdomain_request_list", + "summary": "List platform subdomain requests for a makerspace", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Admin hosting" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaginatedSubdomainRequestList" } } }, "description": "" }, - "404": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/SubdomainRequestError" } } }, "description": "" }, - "409": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/SubdomainRequestError" } } }, "description": "" } } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/memberships/{membership_id}/role": { - "patch": { - "operationId": "api_v1_admin_makerspaces_memberships_role_partial_update", + }, + "post": { + "operationId": "api_v1_admin_makerspace_subdomain_request_create", + "summary": "Request a platform subdomain for a makerspace", "parameters": [ { "in": "path", @@ -35616,37 +35846,30 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "membership_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Admin memberships" + "Admin hosting" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedMembershipRoleAssign" + "$ref": "#/components/schemas/SubdomainRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedMembershipRoleAssign" + "$ref": "#/components/schemas/SubdomainRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedMembershipRoleAssign" + "$ref": "#/components/schemas/SubdomainRequest" } } - } + }, + "required": true }, "security": [ { @@ -35654,11 +35877,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipList" + "$ref": "#/components/schemas/SubdomainRequest" } } }, @@ -35668,17 +35891,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/SubdomainRequestError" } } }, @@ -35688,7 +35901,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/SubdomainRequestError" } } }, @@ -35698,17 +35911,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/SubdomainRequestError" } } }, @@ -35717,10 +35920,10 @@ } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/report-schedules": { + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/disclosure-approvals": { "get": { - "operationId": "api_v1_admin_makerspaces_report_schedules_list", - "summary": "List report schedules", + "operationId": "api_v1_admin_makerspace_tenant_migration_disclosure_approvals_list", + "summary": "List disclosure approvals", "parameters": [ { "in": "path", @@ -35729,19 +35932,10 @@ "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } } ], "tags": [ - "Reports" + "Tenant migration" ], "security": [ { @@ -35753,27 +35947,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedReportScheduleList" + "type": "array", + "items": { + "$ref": "#/components/schemas/ClosureApproval" + } } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Invalid schedule or module disabled." - }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, @@ -35783,47 +35970,37 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Permission denied." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Makerspace or schedule not found." + "description": "Superadmin access required." }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Tenant writes are frozen." + "description": "Throttle limit exceeded." }, - "503": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Tenant write gate unavailable." + "description": "Unexpected migration failure." } } }, "post": { - "operationId": "api_v1_admin_makerspaces_report_schedules_create", - "summary": "Create report schedule", + "operationId": "api_v1_admin_makerspace_tenant_migration_disclosure_approvals_create", + "summary": "Approve each identity in one exact disclosure closure", "parameters": [ { "in": "path", @@ -35835,23 +36012,23 @@ } ], "tags": [ - "Reports" + "Tenant migration" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportSchedule" + "$ref": "#/components/schemas/ClosureApprovalCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ReportSchedule" + "$ref": "#/components/schemas/ClosureApprovalCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ReportSchedule" + "$ref": "#/components/schemas/ClosureApprovalCreate" } } }, @@ -35867,7 +36044,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportSchedule" + "$ref": "#/components/schemas/ClosureApproval" } } }, @@ -35877,70 +36054,79 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/FieldValidationError" } } }, - "description": "Invalid schedule or module disabled." + "description": "Field-keyed validation errors." }, - "401": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Authentication required." + "description": "State conflict." }, - "403": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Permission denied." + "description": "Authentication required." }, - "404": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Makerspace or schedule not found." + "description": "Superadmin access required." }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Tenant writes are frozen." + "description": "Throttle limit exceeded." }, - "503": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Tenant write gate unavailable." + "description": "Unexpected migration failure." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/roles": { - "get": { - "operationId": "api_v1_admin_makerspaces_roles_list", - "description": "Requires manage_makerspace for all roles, or manage_members for action-less community roles only.", + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/disclosure-approvals/{approval_id}/revoke": { + "post": { + "operationId": "api_v1_admin_makerspace_tenant_migration_disclosure_approvals_revoke_create", + "summary": "Revoke a disclosure approval", "parameters": [ + { + "in": "path", + "name": "approval_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -35951,7 +36137,7 @@ } ], "tags": [ - "Admin roles" + "Tenant migration" ], "security": [ { @@ -35963,69 +36149,69 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Role" - } + "$ref": "#/components/schemas/ClosureApproval" } } }, "description": "" }, - "400": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Not found." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "409": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } - }, - "post": { - "operationId": "api_v1_admin_makerspaces_roles_create", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/disclosure-closure": { + "get": { + "operationId": "api_v1_admin_makerspace_tenant_migration_disclosure_closure_retrieve", + "summary": "Compute the pending PORTABLE disclosure closure", "parameters": [ { "in": "path", @@ -36037,49 +36223,19 @@ } ], "tags": [ - "Admin roles" + "Tenant migration" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoleCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/RoleCreate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RoleCreate" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Role" - } - } - }, - "description": "" - }, - "400": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PendingClosure" } } }, @@ -36089,48 +36245,49 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "409": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/roles/{role_id}": { + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/exports": { "get": { - "operationId": "api_v1_admin_makerspaces_roles_retrieve", + "operationId": "api_v1_admin_makerspace_tenant_migration_exports_list", + "summary": "List PORTABLE migration export jobs", "parameters": [ { "in": "path", @@ -36139,18 +36296,10 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "role_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Admin roles" + "Tenant migration" ], "security": [ { @@ -36162,17 +36311,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Role" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/MigrationExportJob" + } } } }, @@ -36182,46 +36324,47 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "409": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } }, - "patch": { - "operationId": "api_v1_admin_makerspaces_roles_partial_update", + "post": { + "operationId": "api_v1_admin_makerspace_tenant_migration_exports_create", + "summary": "Create a target-recipient-encrypted PORTABLE export", "parameters": [ { "in": "path", @@ -36230,37 +36373,30 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "role_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Admin roles" + "Tenant migration" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedRoleWrite" + "$ref": "#/components/schemas/MigrationExportCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedRoleWrite" + "$ref": "#/components/schemas/MigrationExportCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedRoleWrite" + "$ref": "#/components/schemas/MigrationExportCreate" } } - } + }, + "required": true }, "security": [ { @@ -36268,11 +36404,11 @@ } ], "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Role" + "$ref": "#/components/schemas/MigrationExportJob" } } }, @@ -36282,68 +36418,85 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/FieldValidationError" } } }, - "description": "" + "description": "Field-keyed validation errors." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "State conflict." + }, + "503": { + "description": "Migration worker unavailable." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "409": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } - }, - "delete": { - "operationId": "api_v1_admin_makerspaces_roles_destroy", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/exports/{job_id}": { + "get": { + "operationId": "api_v1_admin_makerspace_tenant_migration_exports_retrieve", + "summary": "Read a PORTABLE migration export job", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "job_id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true }, { "in": "path", - "name": "role_id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -36351,7 +36504,7 @@ } ], "tags": [ - "Admin roles" + "Tenant migration" ], "security": [ { @@ -36359,78 +36512,86 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "400": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/MigrationExportJob" } } }, "description": "" }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Not found." + }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "409": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/roles/{role_id}/machine-scope": { - "get": { - "operationId": "api_v1_admin_makerspaces_roles_machine_scope_retrieve", - "description": "Which machines a role's MANAGE_MACHINES grant reaches.\n\nConsole parity: machine scoping fails closed, so without this surface a Space Manager\ncould create a machine-managing role and have no way to make it able to manage\nanything — the capability would exist only in `/control/` and the shell.", + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/exports/{job_id}/download-url": { + "post": { + "operationId": "api_v1_admin_makerspace_tenant_migration_exports_download_url_create", + "summary": "Issue the one-use PORTABLE archive download URL", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "job_id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true }, { "in": "path", - "name": "role_id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -36438,7 +36599,7 @@ } ], "tags": [ - "Admin roles" + "Tenant migration" ], "security": [ { @@ -36450,7 +36611,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RoleMachineScope" + "$ref": "#/components/schemas/DataExportDownloadUrl" } } }, @@ -36460,69 +36621,82 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "State conflict." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Not found." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "409": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } - }, - "put": { - "operationId": "api_v1_admin_makerspaces_roles_machine_scope_update", - "description": "Which machines a role's MANAGE_MACHINES grant reaches.\n\nConsole parity: machine scoping fails closed, so without this surface a Space Manager\ncould create a machine-managing role and have no way to make it able to manage\nanything — the capability would exist only in `/control/` and the shell.", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/exports/{job_id}/quiesce": { + "post": { + "operationId": "api_v1_admin_makerspace_tenant_migration_exports_quiesce_create", + "summary": "Reassert a completed export's source gate lease", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "job_id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true }, { "in": "path", - "name": "role_id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -36530,28 +36704,8 @@ } ], "tags": [ - "Admin roles" + "Tenant migration" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RoleMachineScopeWrite" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/RoleMachineScopeWrite" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RoleMachineScopeWrite" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -36562,68 +36716,79 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RoleMachineScope" + "$ref": "#/components/schemas/CutoverOutcome" } } }, "description": "" }, - "400": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "State conflict." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "409": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/roles/capabilities": { - "get": { - "operationId": "api_v1_admin_makerspaces_roles_capabilities_list", + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/pairings/{pairing_id}/archive-source": { + "post": { + "operationId": "api_v1_admin_makerspace_tenant_migration_pairings_archive_source_create", + "summary": "Archive the quiesced source and issue its signed cutover receipt", "parameters": [ { "in": "path", @@ -36632,10 +36797,19 @@ "type": "integer" }, "required": true + }, + { + "in": "path", + "name": "pairing_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true } ], "tags": [ - "Admin roles" + "Tenant migration" ], "security": [ { @@ -36647,71 +36821,79 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Capability" - } + "$ref": "#/components/schemas/CutoverOutcome" } } }, "description": "" }, - "400": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "State conflict." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "409": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/saved-report-views": { - "get": { - "operationId": "api_v1_admin_makerspaces_saved_report_views_retrieve", + "/api/v1/admin/makerspace/{makerspace_id}/tenant-migration/pairings/{pairing_id}/recover": { + "post": { + "operationId": "api_v1_admin_makerspace_tenant_migration_pairings_recover_create", + "summary": "Recover an archived source with the target abort receipt", "parameters": [ { "in": "path", @@ -36722,16 +36904,38 @@ "required": true }, { - "in": "query", - "name": "page", + "in": "path", + "name": "pairing_id", "schema": { - "type": "integer" - } + "type": "string", + "format": "uuid" + }, + "required": true } ], "tags": [ - "Reports" + "Tenant migration" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CutoverReceiptRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CutoverReceiptRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CutoverReceiptRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -36742,7 +36946,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedReportViewList" + "$ref": "#/components/schemas/CutoverOutcome" } } }, @@ -36752,66 +36956,79 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/FieldValidationError" } } }, - "description": "" + "description": "Field-keyed validation errors." }, - "401": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Not found." }, - "403": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "State conflict." }, - "404": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, - "423": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "503": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } - }, - "post": { - "operationId": "api_v1_admin_makerspaces_saved_report_views_create", + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/usage": { + "get": { + "operationId": "api_v1_admin_makerspace_usage_retrieve", + "summary": "Read own makerspace usage and effective limits", "parameters": [ { "in": "path", @@ -36823,118 +37040,94 @@ } ], "tags": [ - "Reports" + "Admin makerspaces" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SavedReportView" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SavedReportView" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SavedReportView" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedReportView" + "$ref": "#/components/schemas/MakerspaceUsage" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/verify-domain": { + "post": { + "operationId": "api_v1_admin_makerspace_verify_domain_create", + "summary": "Verify a makerspace custom domain", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, - "description": "" - }, - "423": { + "required": true + } + ], + "tags": [ + "Admin makerspaces" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/DomainVerificationResponse" } } }, "description": "" }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "403": { + "description": "Missing makerspace management permission." + }, + "404": { + "description": "Makerspace not found." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/saved-report-views/{id}": { + "/api/v1/admin/makerspace/{makerspace_id}/warranties": { "get": { - "operationId": "api_v1_admin_makerspaces_saved_report_views_retrieve_2", + "operationId": "api_v1_admin_makerspace_warranties_list", + "summary": "List warranty coverage for a makerspace", "parameters": [ { - "in": "path", - "name": "id", + "in": "query", + "name": "expires_before", "schema": { - "type": "integer" + "type": "string", + "format": "date" }, - "required": true + "description": "Only include hosts with warranty expiry on or before this date." }, { "in": "path", @@ -36943,10 +37136,50 @@ "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "missing_docs", + "schema": { + "type": "boolean" + }, + "description": "Only include hosts with no warranty documents or no warranty record." + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + }, + { + "name": "page_size", + "required": false, + "in": "query", + "description": "Number of results to return per page.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "active", + "expired", + "expiring_soon", + "unknown" + ] + }, + "description": "Filter rows by computed warranty status." } ], "tags": [ - "Reports" + "Admin warranty" ], "security": [ { @@ -36958,17 +37191,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedReportView" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/PaginatedWarrantyReportRowList" } } }, @@ -36978,7 +37201,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -36988,7 +37211,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -36998,36 +37221,136 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - }, - "423": { + } + } + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/whatsapp-templates": { + "get": { + "operationId": "api_v1_admin_makerspace_whatsapp_templates_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Messaging" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/WhatsAppBindingPage" } } }, "description": "" }, - "503": { + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + }, + "post": { + "operationId": "api_v1_admin_makerspace_whatsapp_templates_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Messaging" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WhatsAppBinding" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WhatsAppBinding" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WhatsAppBinding" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/WhatsAppBinding" } } }, "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." } } - }, + } + }, + "/api/v1/admin/makerspace/{makerspace_id}/whatsapp-templates/{id}": { "patch": { - "operationId": "api_v1_admin_makerspaces_saved_report_views_partial_update", + "operationId": "api_v1_admin_makerspace_whatsapp_templates_partial_update", "parameters": [ { "in": "path", @@ -37047,23 +37370,23 @@ } ], "tags": [ - "Reports" + "Messaging" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedSavedReportView" + "$ref": "#/components/schemas/PatchedWhatsAppBinding" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedSavedReportView" + "$ref": "#/components/schemas/PatchedWhatsAppBinding" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedSavedReportView" + "$ref": "#/components/schemas/PatchedWhatsAppBinding" } } } @@ -37078,76 +37401,37 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedReportView" + "$ref": "#/components/schemas/WhatsAppBinding" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." }, "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Makerspace writes are frozen for tenant migration." }, "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "A required dependency is unavailable." } } }, "delete": { - "operationId": "api_v1_admin_makerspaces_saved_report_views_destroy", + "operationId": "api_v1_admin_makerspace_whatsapp_templates_destroy", "parameters": [ { "in": "path", @@ -37167,7 +37451,7 @@ } ], "tags": [ - "Reports" + "Messaging" ], "security": [ { @@ -37178,90 +37462,28 @@ "204": { "description": "No response body" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" - }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "Makerspace writes are frozen for tenant migration." }, "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "" + "description": "A required dependency is unavailable." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/saved-views": { + "/api/v1/admin/makerspace/{makerspace_id}/whatsapp-templates/catalog": { "get": { - "operationId": "api_v1_admin_makerspaces_saved_views_retrieve", + "operationId": "api_v1_admin_makerspace_whatsapp_templates_catalog_list", "parameters": [ - { - "in": "query", - "name": "entity", - "schema": { - "type": "string", - "enum": [ - "audit", - "bookings", - "events", - "inventory", - "members", - "payments", - "requests" - ] - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -37269,17 +37491,10 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } } ], "tags": [ - "Staff lists" + "Messaging" ], "security": [ { @@ -37291,104 +37506,80 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedViewList" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/MessagingCatalogEntry" + } } } }, "description": "" }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/admin/makerspaces": { + "get": { + "operationId": "api_v1_admin_makerspaces_list", + "summary": "List makerspaces the actor has a staff surface in", + "tags": [ + "Admin makerspaces" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/Makerspace" + } } } }, "description": "" }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "403": { + "description": "Not an active staff account." } } }, "post": { - "operationId": "api_v1_admin_makerspaces_saved_views_create", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_admin_makerspaces_create", + "description": "Exactly one write-only organization_id (existing active owner organization) or organization_name (new owner organization) is required; both are refused on update. Owner assignment and makerspace creation are atomic. Optional install_profile_id applies a saved pack; an unknown profile returns 404 before owner assignment. creation_setup applies profile, hours, modules and first staff invitation in the same transaction; explicit setup overrides the pack. Invitation delivery follows commit and returns a private fallback link.", + "summary": "Create a makerspace (superadmin only)", "tags": [ - "Staff lists" + "Admin makerspaces" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedView" + "$ref": "#/components/schemas/Makerspace" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/SavedView" + "$ref": "#/components/schemas/Makerspace" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/SavedView" + "$ref": "#/components/schemas/Makerspace" } } }, @@ -37404,7 +37595,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedView" + "$ref": "#/components/schemas/Makerspace" } } }, @@ -37414,33 +37605,120 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {}, + "properties": { + "theme_config": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "object", + "required": [ + "code", + "messages" + ], + "properties": { + "code": { + "type": "string", + "enum": [ + "theme_contrast" + ] + }, + "messages": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + ] + } + } } } }, - "description": "" + "description": "Field validation errors. Contrast errors have theme_config: {code: 'theme_contrast', messages: string[]} with mode, failing pair, measured ratio and required ratio." }, "401": { + "description": "Not signed in." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + }, + "403": { + "description": "Only a superadmin can create a makerspace." + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/booking-machine-types/": { + "get": { + "operationId": "api_v1_admin_makerspaces_booking_machine_types_retrieve", + "summary": "List machine type choices for booking spaces", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number." + } + ], + "tags": [ + "Admin bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/BookingMachineTypePage" } } }, "description": "" }, - "403": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "" + "description": "Bookings or machines module disabled." }, - "404": { + "401": { "content": { "application/json": { "schema": { @@ -37448,9 +37726,9 @@ } } }, - "description": "" + "description": "Authentication required." }, - "423": { + "403": { "content": { "application/json": { "schema": { @@ -37458,9 +37736,9 @@ } } }, - "description": "" + "description": "Permission denied." }, - "503": { + "404": { "content": { "application/json": { "schema": { @@ -37468,34 +37746,33 @@ } } }, - "description": "" + "description": "Not found." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/saved-views/{id}": { + "/api/v1/admin/makerspaces/{makerspace_id}/event-categories/": { "get": { - "operationId": "api_v1_admin_makerspaces_saved_views_retrieve_2", + "operationId": "api_v1_admin_makerspaces_event_categories_retrieve", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "page", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Staff lists" + "Admin events" ], "security": [ { @@ -37507,7 +37784,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedView" + "$ref": "#/components/schemas/CategoryList" } } }, @@ -37517,11 +37794,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "" + "description": "Invalid field values, disabled events module or disabled feature." }, "401": { "content": { @@ -37553,6 +37831,16 @@ }, "description": "" }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, "423": { "content": { "application/json": { @@ -37563,6 +37851,16 @@ }, "description": "" }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, "503": { "content": { "application/json": { @@ -37575,17 +37873,9 @@ } } }, - "patch": { - "operationId": "api_v1_admin_makerspaces_saved_views_partial_update", + "post": { + "operationId": "api_v1_admin_makerspaces_event_categories_create", "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -37596,26 +37886,27 @@ } ], "tags": [ - "Staff lists" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedSavedView" + "$ref": "#/components/schemas/CategoryInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedSavedView" + "$ref": "#/components/schemas/CategoryInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedSavedView" + "$ref": "#/components/schemas/CategoryInput" } } - } + }, + "required": true }, "security": [ { @@ -37623,11 +37914,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SavedView" + "$ref": "#/components/schemas/Category" } } }, @@ -37637,11 +37928,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } } }, - "description": "" + "description": "Invalid field values, disabled events module or disabled feature." }, "401": { "content": { @@ -37673,6 +37965,16 @@ }, "description": "" }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, "423": { "content": { "application/json": { @@ -37683,6 +37985,16 @@ }, "description": "" }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, "503": { "content": { "application/json": { @@ -37694,18 +38006,13 @@ "description": "" } } - }, - "delete": { - "operationId": "api_v1_admin_makerspaces_saved_views_destroy", + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/event-collaborations/": { + "get": { + "operationId": "api_v1_admin_makerspaces_event_collaborations_list", + "summary": "List collaboration invitations for a makerspace", "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -37716,7 +38023,7 @@ } ], "tags": [ - "Staff lists" + "Admin events" ], "security": [ { @@ -37724,20 +38031,20 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "400": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/EventCollaborationInbox" + } } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -37745,7 +38052,7 @@ } } }, - "description": "" + "description": "Invalid collaboration request." }, "403": { "content": { @@ -37755,7 +38062,7 @@ } } }, - "description": "" + "description": "Event management access denied." }, "404": { "content": { @@ -37765,35 +38072,15 @@ } } }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Event collaboration not found." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/spaces/": { + "/api/v1/admin/makerspaces/{makerspace_id}/event-series/": { "get": { - "operationId": "api_v1_admin_makerspaces_spaces_retrieve", - "summary": "List bookable spaces in a makerspace", + "operationId": "api_v1_admin_makerspaces_event_series_retrieve", + "summary": "List recurring event series", "parameters": [ { "in": "path", @@ -37802,34 +38089,10 @@ "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "ordering", - "schema": { - "type": "string" - }, - "description": "Allowlisted sort field; prefix with '-' for descending." - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - }, - "description": "Page number, 24 rows per page." - }, - { - "in": "query", - "name": "q", - "schema": { - "type": "string" - }, - "description": "Search within the authorized list." } ], "tags": [ - "Admin bookings" + "Admin event series" ], "security": [ { @@ -37841,7 +38104,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceListResponse" + "$ref": "#/components/schemas/EventSeriesListResponse" } } }, @@ -37855,7 +38118,7 @@ } } }, - "description": "Invalid request." + "description": "Invalid recurring event series." }, "401": { "content": { @@ -37875,7 +38138,7 @@ } } }, - "description": "Permission denied." + "description": "Event management access denied." }, "404": { "content": { @@ -37885,13 +38148,33 @@ } } }, - "description": "Not found." + "description": "Event series not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Series state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." } } }, "post": { - "operationId": "api_v1_admin_makerspaces_spaces_create", - "summary": "Create a bookable space", + "operationId": "api_v1_admin_makerspaces_event_series_create", + "summary": "Create a recurring event series", "parameters": [ { "in": "path", @@ -37903,23 +38186,23 @@ } ], "tags": [ - "Admin bookings" + "Admin event series" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceWrite" + "$ref": "#/components/schemas/EventSeriesWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/BookableSpaceWrite" + "$ref": "#/components/schemas/EventSeriesWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/BookableSpaceWrite" + "$ref": "#/components/schemas/EventSeriesWrite" } } }, @@ -37935,7 +38218,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceAdmin" + "$ref": "#/components/schemas/EventSeriesMutationResponse" } } }, @@ -37949,7 +38232,7 @@ } } }, - "description": "Invalid request." + "description": "Invalid recurring event series." }, "401": { "content": { @@ -37969,7 +38252,7 @@ } } }, - "description": "Permission denied." + "description": "Event management access denied." }, "404": { "content": { @@ -37979,14 +38262,34 @@ } } }, - "description": "Not found." + "description": "Event series not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Series state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/staff-invitations": { + "/api/v1/admin/makerspaces/{makerspace_id}/event-series-collaborations/": { "get": { - "operationId": "api_v1_admin_makerspaces_staff_invitations_list", + "operationId": "api_v1_admin_makerspaces_event_series_collaborations_list", "parameters": [ { "in": "path", @@ -37995,19 +38298,146 @@ "type": "integer" }, "required": true - }, - { - "name": "page", - "required": false, + } + ], + "tags": [ + "Admin event series" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SeriesCollaborationInbox" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid collaboration request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event management access denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Series collaboration not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Collaboration state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/events/": { + "get": { + "operationId": "api_v1_admin_makerspaces_events_retrieve", + "summary": "List events in a makerspace", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { "in": "query", - "description": "A page number within the paginated result set.", + "name": "ordering", + "schema": { + "type": "string" + }, + "description": "Allowlisted sort field; prefix with '-' for descending." + }, + { + "in": "query", + "name": "page", "schema": { "type": "integer" + }, + "description": "Page number, 24 rows per page." + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Search within the authorized list." + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "cancelled", + "completed", + "draft", + "published" + ] } } ], "tags": [ - "Staff invitations" + "Admin events" ], "security": [ { @@ -38019,31 +38449,57 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedStaffInvitationList" + "$ref": "#/components/schemas/EventListWithDefaults" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } }, "post": { - "operationId": "api_v1_admin_makerspaces_staff_invitations_create", + "operationId": "api_v1_admin_makerspaces_events_create", + "summary": "Create a draft event", "parameters": [ { "in": "path", @@ -38055,23 +38511,23 @@ } ], "tags": [ - "Staff invitations" + "Admin events" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffInvitationCreate" + "$ref": "#/components/schemas/EventWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/StaffInvitationCreate" + "$ref": "#/components/schemas/EventWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/StaffInvitationCreate" + "$ref": "#/components/schemas/EventWrite" } } }, @@ -38087,48 +38543,61 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffInvitationDelivery" + "$ref": "#/components/schemas/EventAdmin" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid event details." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." - }, - "503": { - "description": "A required dependency is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/staff-invitations/{invitation_id}/resend": { - "post": { - "operationId": "api_v1_admin_makerspaces_staff_invitations_resend_create", + "/api/v1/admin/makerspaces/{makerspace_id}/evidence-retention": { + "get": { + "operationId": "api_v1_admin_makerspaces_evidence_retention_retrieve", + "summary": "Get a makerspace evidence retention and abandoned-upload policy", "parameters": [ - { - "in": "path", - "name": "invitation_id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -38139,7 +38608,7 @@ } ], "tags": [ - "Staff invitations" + "Evidence retention" ], "security": [ { @@ -38151,48 +38620,30 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffInvitationDelivery" + "$ref": "#/components/schemas/EvidenceRetentionPolicy" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, "401": { - "description": "Not signed in." + "description": "Authentication is required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Active manage-makerspace permission is required." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "description": "Makerspace was not found in the actor's scope." }, "503": { - "description": "A required dependency is unavailable." + "description": "Deployment recovery is active." } } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/staff-invitations/{invitation_id}/revoke": { - "post": { - "operationId": "api_v1_admin_makerspaces_staff_invitations_revoke_create", + }, + "patch": { + "operationId": "api_v1_admin_makerspaces_evidence_retention_partial_update", + "summary": "Partially update the evidence retention and abandoned-upload policy", "parameters": [ - { - "in": "path", - "name": "invitation_id", - "schema": { - "type": "integer" - }, - "required": true - }, { "in": "path", "name": "makerspace_id", @@ -38203,8 +38654,27 @@ } ], "tags": [ - "Staff invitations" + "Evidence retention" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedEvidenceRetentionPatch" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedEvidenceRetentionPatch" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedEvidenceRetentionPatch" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -38215,40 +38685,34 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffInvitation" + "$ref": "#/components/schemas/EvidenceRetentionPolicy" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "description": "Retention days are invalid." }, "401": { - "description": "Not signed in." + "description": "Authentication is required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Active manage-makerspace permission is required." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "description": "Makerspace was not found in the actor's scope." }, "503": { - "description": "A required dependency is unavailable." + "description": "Deployment recovery is active." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/uploads/evidence-url": { + "/api/v1/admin/makerspaces/{makerspace_id}/evidence-retention/preview": { "post": { - "operationId": "api_v1_admin_makerspaces_uploads_evidence_url_create", - "description": "Base for ALL staff endpoints: authenticated + active staff + auto-scoped queryset.\n\nFuture phases subclass this so the invariant 'every staff query is makerspace-scoped'\nis enforced by default rather than by remembering to add a mixin (review fix #4). Add\n`required_action` + `HasMakerspaceAction` to a subclass for per-action checks.", + "operationId": "api_v1_admin_makerspaces_evidence_retention_preview_create", + "summary": "Preview evidence object expiry and abandoned-upload cleanup", "parameters": [ { "in": "path", @@ -38260,27 +38724,26 @@ } ], "tags": [ - "api" + "Evidence retention" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EvidenceUrlRequest" + "$ref": "#/components/schemas/EvidenceRetentionPreviewRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/EvidenceUrlRequest" + "$ref": "#/components/schemas/EvidenceRetentionPreviewRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/EvidenceUrlRequest" + "$ref": "#/components/schemas/EvidenceRetentionPreviewRequest" } } - }, - "required": true + } }, "security": [ { @@ -38288,32 +38751,39 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EvidenceUrlResponse" + "$ref": "#/components/schemas/EvidenceRetentionPreviewResponse" } } }, "description": "" }, "400": { - "description": "Invalid evidence upload request." + "description": "Preview limit is invalid." + }, + "401": { + "description": "Authentication is required." }, "403": { - "description": "Insufficient makerspace permission." + "description": "Active manage-makerspace permission is required." + }, + "404": { + "description": "Makerspace was not found in the actor's scope." }, "503": { - "description": "Evidence storage is unavailable." + "description": "Deployment recovery is active." } } } }, - "/api/v1/admin/makerspaces/{makerspace_id}/waiver": { + "/api/v1/admin/makerspaces/{makerspace_id}/invitation-requests": { "get": { - "operationId": "api_v1_admin_makerspaces_waiver_retrieve", - "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", + "operationId": "api_v1_admin_makerspaces_invitation_requests_list", + "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", + "summary": "List invitation requests (optionally filtered by ?status=)", "parameters": [ { "in": "path", @@ -38337,7 +38807,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberWaiverResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/InvitationRequest" + } } } }, @@ -38392,33 +38865,21 @@ } }, "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" } } - }, - "put": { - "operationId": "api_v1_admin_makerspaces_waiver_update", - "description": "Requires manage_makerspace in this makerspace to publish or clear waiver text.", + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/machine-materials": { + "get": { + "operationId": "api_v1_admin_makerspaces_machine_materials_list", "parameters": [ + { + "in": "query", + "name": "machine_type_id", + "schema": { + "type": "integer" + } + }, { "in": "path", "name": "makerspace_id", @@ -38426,30 +38887,20 @@ "type": "integer" }, "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } } ], "tags": [ - "Admin memberships" + "api" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WaiverPublish" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/WaiverPublish" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/WaiverPublish" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -38460,13 +38911,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberWaiverResponse" + "$ref": "#/components/schemas/PaginatedMaterialList" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -38476,7 +38927,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -38486,7 +38937,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -38496,7 +38947,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -38506,7 +38957,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -38516,7 +38967,7 @@ }, "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -38526,23 +38977,21 @@ }, "description": "" }, - "503": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, "description": "" } } - } - }, - "/api/v1/admin/makerspaces/{makerspace_id}/walk-in-members": { - "get": { - "operationId": "api_v1_admin_makerspaces_walk_in_members_retrieve", - "summary": "Read walk-in admission questions", + }, + "post": { + "operationId": "api_v1_admin_makerspaces_machine_materials_create", "parameters": [ { "in": "path", @@ -38554,25 +39003,45 @@ } ], "tags": [ - "Admin memberships" + "api" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Material" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Material" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Material" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicAdmission" + "$ref": "#/components/schemas/Material" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -38582,7 +39051,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -38592,7 +39061,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -38602,7 +39071,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -38612,7 +39081,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -38622,7 +39091,7 @@ }, "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -38632,22 +39101,32 @@ }, "description": "" }, - "503": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, "description": "" } } - }, - "post": { - "operationId": "api_v1_admin_makerspaces_walk_in_members_create", - "summary": "Create a walk-in member record", + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/machine-materials/{id}": { + "get": { + "operationId": "api_v1_admin_makerspaces_machine_materials_list_2", "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "makerspace_id", @@ -38655,48 +39134,37 @@ "type": "integer" }, "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } } ], "tags": [ - "Admin memberships" + "api" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WalkInMemberCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/WalkInMemberCreate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/WalkInMemberCreate" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WalkInAdmissionResult" + "$ref": "#/components/schemas/PaginatedMaterialList" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -38706,7 +39174,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -38716,7 +39184,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -38726,7 +39194,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -38736,7 +39204,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -38746,7 +39214,7 @@ }, "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -38756,23 +39224,21 @@ }, "description": "" }, - "503": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, "description": "" } } - } - }, - "/api/v1/admin/makerspaces/{id}": { - "get": { - "operationId": "api_v1_admin_makerspaces_retrieve", - "summary": "Retrieve a makerspace's settings", + }, + "patch": { + "operationId": "api_v1_admin_makerspaces_machine_materials_partial_update", "parameters": [ { "in": "path", @@ -38781,43 +39247,10 @@ "type": "integer" }, "required": true - } - ], - "tags": [ - "Admin makerspaces" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Makerspace" - } - } - }, - "description": "" - }, - "401": { - "description": "Authentication required." }, - "404": { - "description": "Makerspace not found, archived, not active, hidden from superadmins, or outside the actor's scope for this method." - } - } - }, - "patch": { - "operationId": "api_v1_admin_makerspaces_partial_update", - "description": "Needs manage_makerspace. `enabled_modules` is superadmin-owned and edited only in /control/ or the module console: a body carrying it is refused with 403.", - "summary": "Update a makerspace's settings", - "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -38825,23 +39258,23 @@ } ], "tags": [ - "Admin makerspaces" + "api" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedMakerspace" + "$ref": "#/components/schemas/PatchedMaterial" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedMakerspace" + "$ref": "#/components/schemas/PatchedMaterial" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedMakerspace" + "$ref": "#/components/schemas/PatchedMaterial" } } } @@ -38856,103 +39289,43 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Makerspace" + "$ref": "#/components/schemas/Material" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {}, - "properties": { - "theme_config": { - "oneOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "object", - "required": [ - "code", - "messages" - ], - "properties": { - "code": { - "type": "string", - "enum": [ - "theme_contrast" - ] - }, - "messages": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - ] - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Field validation errors. Contrast errors have theme_config: {code: 'theme_contrast', messages: string[]} with mode, failing pair, measured ratio and required ratio." - }, - "401": { - "description": "Authentication required." + "description": "" }, "403": { - "description": "The body carries `enabled_modules`." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Makerspace not found, archived, not active, hidden from superadmins, or outside the actor's scope for this method." - } - } - } - }, - "/api/v1/admin/member-cards/{id}/print.pdf": { - "post": { - "operationId": "api_v1_admin_member_cards_print.pdf_create", - "summary": "Print one card (CR80 PDF)", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin memberships" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { - "application/pdf": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "423": { "content": { "application/json": { "schema": { @@ -38960,9 +39333,9 @@ } } }, - "description": "Invalid card input or membership module disabled." + "description": "" }, - "401": { + "429": { "content": { "application/json": { "schema": { @@ -38970,9 +39343,9 @@ } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "503": { "content": { "application/json": { "schema": { @@ -38980,25 +39353,23 @@ } } }, - "description": "Missing member-card action or inactive account." + "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, - "description": "Not found in the permitted makerspace." + "description": "" } } - } - }, - "/api/v1/admin/member-cards/{id}/reissue": { - "post": { - "operationId": "api_v1_admin_member_cards_reissue_create", - "summary": "Reissue (rotate the QR of) a card", + }, + "delete": { + "operationId": "api_v1_admin_makerspaces_machine_materials_destroy", "parameters": [ { "in": "path", @@ -39007,48 +39378,39 @@ "type": "integer" }, "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true } ], "tags": [ - "Admin memberships" + "api" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MemberCardReissue" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MemberCardReissue" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MemberCardReissue" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCard" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "403": { "content": { "application/json": { "schema": { @@ -39056,9 +39418,9 @@ } } }, - "description": "Invalid card input or membership module disabled." + "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { @@ -39066,9 +39428,9 @@ } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "423": { "content": { "application/json": { "schema": { @@ -39076,9 +39438,9 @@ } } }, - "description": "Missing member-card action or inactive account." + "description": "" }, - "404": { + "429": { "content": { "application/json": { "schema": { @@ -39086,19 +39448,75 @@ } } }, - "description": "Not found in the permitted makerspace." + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" } } } }, - "/api/v1/admin/member-cards/{id}/revoke": { + "/api/v1/admin/makerspaces/{makerspace_id}/machine-service/consumable-pools": { + "get": { + "operationId": "api_v1_admin_makerspaces_machine_service_consumable_pools_list", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machine service" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PrinterPool" + } + } + } + }, + "description": "" + } + } + }, "post": { - "operationId": "api_v1_admin_member_cards_revoke_create", - "summary": "Revoke a card (redacts name and photo)", + "operationId": "api_v1_admin_makerspaces_machine_service_consumable_pools_create", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -39106,26 +39524,27 @@ } ], "tags": [ - "Admin memberships" + "Admin machine service" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardRevoke" + "$ref": "#/components/schemas/PrinterPoolCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberCardRevoke" + "$ref": "#/components/schemas/PrinterPoolCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberCardRevoke" + "$ref": "#/components/schemas/PrinterPoolCreate" } } - } + }, + "required": true }, "security": [ { @@ -39133,27 +39552,98 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCard" + "$ref": "#/components/schemas/PrinterPool" } } }, "description": "" + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/machine-service/requests": { + "get": { + "operationId": "api_v1_admin_makerspaces_machine_service_requests_list", + "summary": "List machine service requests", + "parameters": [ + { + "in": "query", + "name": "bucket", + "schema": { + "type": "integer" + } }, - "400": { + { + "in": "query", + "name": "machine", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "machine_type", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "machine_type_id", + "schema": { + "type": "integer" + } + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "queue", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + } + ], + "tags": [ + "Admin machine service" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineServiceRequest" + } } } }, - "description": "Invalid card input or membership module disabled." + "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -39161,19 +39651,18 @@ } } }, + "description": "Invalid service request input." + }, + "401": { "description": "Authentication required." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Missing member-card action or inactive account." + "description": "Machine management permission required." }, "404": { + "description": "Service request was not found." + }, + "409": { "content": { "application/json": { "schema": { @@ -39181,20 +39670,17 @@ } } }, - "description": "Not found in the permitted makerspace." + "description": "Service workflow conflict." } } - } - }, - "/api/v1/admin/membership-plans/{id}": { - "patch": { - "operationId": "api_v1_admin_membership_plans_partial_update", - "description": "Requires manage_makerspace in this makerspace; manage_members alone cannot change plan definitions or prices.", - "summary": "Update a membership plan", + }, + "post": { + "operationId": "api_v1_admin_makerspaces_machine_service_requests_create", + "summary": "Submit a machine service request for a member", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -39202,26 +39688,27 @@ } ], "tags": [ - "Admin memberships" + "Admin machine service" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedMembershipPlan" + "$ref": "#/components/schemas/MachineServiceSubmit" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedMembershipPlan" + "$ref": "#/components/schemas/MachineServiceSubmit" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedMembershipPlan" + "$ref": "#/components/schemas/MachineServiceSubmit" } } - } + }, + "required": true }, "security": [ { @@ -39229,11 +39716,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipPlan" + "$ref": "#/components/schemas/MachineServiceRequest" } } }, @@ -39247,19 +39734,18 @@ } } }, - "description": "" + "description": "Invalid service request input." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication required." }, "403": { + "description": "Machine management permission required." + }, + "404": { + "description": "Service request was not found." + }, + "409": { "content": { "application/json": { "schema": { @@ -39267,23 +39753,108 @@ } } }, - "description": "" - }, - "404": { + "description": "Service workflow conflict." + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/machine-service/typed-manual-usage": { + "get": { + "operationId": "api_v1_admin_makerspaces_machine_service_typed_manual_usage_list", + "parameters": [ + { + "in": "query", + "name": "machine_type", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "machine_type_id", + "schema": { + "type": "integer" + } + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machine service" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/TypedManualUsageResponse" + } } } }, "description": "" + } + } + }, + "post": { + "operationId": "api_v1_admin_makerspaces_machine_service_typed_manual_usage_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin machine service" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedManualUsage" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/TypedManualUsage" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/TypedManualUsage" + } + } }, - "409": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedManualUsageResponse" } } }, @@ -39292,13 +39863,12 @@ } } }, - "/api/v1/admin/membership-requests": { + "/api/v1/admin/makerspaces/{makerspace_id}/machine-types/{type_id}/intake": { "get": { - "operationId": "api_v1_admin_membership_requests_retrieve", - "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", + "operationId": "api_v1_admin_makerspaces_machine_types_intake_retrieve", "parameters": [ { - "in": "query", + "in": "path", "name": "makerspace_id", "schema": { "type": "integer" @@ -39306,31 +39876,16 @@ "required": true }, { - "in": "query", - "name": "ordering", - "schema": { - "type": "string" - }, - "description": "Allowlisted sort field; prefix with '-' for descending." - }, - { - "in": "query", - "name": "page", + "in": "path", + "name": "type_id", "schema": { "type": "integer" }, - "description": "Page number, 24 rows per page." - }, - { - "in": "query", - "name": "state", - "schema": { - "type": "string" - } + "required": true } ], "tags": [ - "Admin memberships" + "api" ], "security": [ { @@ -39342,13 +39897,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipRequestListResponse" + "$ref": "#/components/schemas/Intake" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -39358,7 +39913,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -39368,7 +39923,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -39378,7 +39933,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -39388,7 +39943,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -39398,7 +39953,7 @@ }, "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -39408,27 +39963,33 @@ }, "description": "" }, - "503": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, "description": "" } } - } - }, - "/api/v1/admin/membership-requests/{id}/approve": { - "post": { - "operationId": "api_v1_admin_membership_requests_approve_create", - "description": "Requires manage_members. Returns an admission request when payment is due, otherwise the activated membership.", + }, + "put": { + "operationId": "api_v1_admin_makerspaces_machine_types_intake_update", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "type_id", "schema": { "type": "integer" }, @@ -39436,23 +39997,23 @@ } ], "tags": [ - "Admin memberships" + "api" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ApproveRequest" + "$ref": "#/components/schemas/Intake" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ApproveRequest" + "$ref": "#/components/schemas/Intake" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ApproveRequest" + "$ref": "#/components/schemas/Intake" } } }, @@ -39468,13 +40029,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipApprovalOutcome" + "$ref": "#/components/schemas/Intake" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -39484,7 +40045,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -39494,7 +40055,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -39504,7 +40065,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -39514,7 +40075,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -39524,7 +40085,7 @@ }, "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -39534,11 +40095,12 @@ }, "description": "" }, - "503": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, @@ -39547,14 +40109,77 @@ } } }, - "/api/v1/admin/membership-requests/{id}/revoke": { + "/api/v1/admin/makerspaces/{makerspace_id}/machines/{machine_id}/maintenance/logs/": { + "get": { + "operationId": "api_v1_admin_makerspaces_machines_maintenance_logs_retrieve", + "summary": "List immutable machine maintenance logs", + "parameters": [ + { + "in": "path", + "name": "machine_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin maintenance" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaintenanceLogList" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + }, "post": { - "operationId": "api_v1_admin_membership_requests_revoke_create", - "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", + "operationId": "api_v1_admin_makerspaces_machines_maintenance_logs_create", + "description": "When schedule_id is supplied, complete that schedule and set its next due date according to its recurrence anchor. Completion anchors add interval_days to the makerspace-local date of performed_at; calendar anchors advance the previous due date by whole intervals past completion. performed_at defaults to now; backdated entries use their recorded completion date.", + "summary": "Record completed machine maintenance", "parameters": [ { "in": "path", - "name": "id", + "name": "machine_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -39562,48 +40187,228 @@ } ], "tags": [ - "Admin memberships" + "Admin maintenance" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Revoke" + "$ref": "#/components/schemas/MaintenanceLogWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Revoke" + "$ref": "#/components/schemas/MaintenanceLogWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Revoke" + "$ref": "#/components/schemas/MaintenanceLogWrite" } } - } + }, + "required": true }, "security": [ { "jwtAuth": [] } ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaintenanceLog" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/machines/{machine_id}/maintenance/schedules/": { + "get": { + "operationId": "api_v1_admin_makerspaces_machines_maintenance_schedules_retrieve", + "summary": "List machine maintenance schedules", + "parameters": [ + { + "in": "path", + "name": "machine_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin maintenance" + ], + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipRequest" + "$ref": "#/components/schemas/MaintenanceScheduleList" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + }, + "post": { + "operationId": "api_v1_admin_makerspaces_machines_maintenance_schedules_create", + "summary": "Create a machine maintenance schedule", + "parameters": [ + { + "in": "path", + "name": "machine_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin maintenance" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaintenanceScheduleWrite" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MaintenanceScheduleWrite" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MaintenanceScheduleWrite" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MaintenanceSchedule" } } }, "description": "" }, "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/maintenance/feed": { + "get": { + "operationId": "api_v1_admin_makerspaces_maintenance_feed_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "api" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/FeedState" } } }, @@ -39639,7 +40444,7 @@ }, "description": "" }, - "409": { + "423": { "content": { "application/json": { "schema": { @@ -39649,7 +40454,7 @@ }, "description": "" }, - "423": { + "429": { "content": { "application/json": { "schema": { @@ -39668,19 +40473,26 @@ } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" } } - } - }, - "/api/v1/admin/membership-terms/{id}/cancel": { + }, "post": { - "operationId": "api_v1_admin_membership_terms_cancel_create", - "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", - "summary": "Cancel an active term", + "operationId": "api_v1_admin_makerspaces_maintenance_feed_create", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -39688,7 +40500,7 @@ } ], "tags": [ - "Admin memberships" + "api" ], "security": [ { @@ -39700,13 +40512,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipTerm" + "$ref": "#/components/schemas/FeedIssued" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -39716,7 +40528,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -39726,7 +40538,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -39736,7 +40548,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -39746,7 +40558,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -39755,65 +40567,44 @@ } }, "description": "" - } - } - } - }, - "/api/v1/admin/memberships": { - "get": { - "operationId": "api_v1_admin_memberships_retrieve", - "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", - "parameters": [ - { - "in": "query", - "name": "id", - "schema": { - "type": "integer" - }, - "description": "Select one membership within the authorized roster." }, - { - "in": "query", - "name": "makerspace_id", - "schema": { - "type": "integer" + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true + "description": "" }, - { - "in": "query", - "name": "ordering", - "schema": { - "type": "string" + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } }, - "description": "Allowlisted sort field; prefix with '-' for descending." - }, + "description": "" + } + } + }, + "delete": { + "operationId": "api_v1_admin_makerspaces_maintenance_feed_destroy", + "parameters": [ { - "in": "query", - "name": "page", + "in": "path", + "name": "makerspace_id", "schema": { "type": "integer" }, - "description": "Page number, 24 rows per page." - }, - { - "in": "query", - "name": "q", - "schema": { - "type": "string" - }, - "description": "Search within the authorized list." - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string" - } + "required": true } ], "tags": [ - "Admin memberships" + "api" ], "security": [ { @@ -39821,17 +40612,10 @@ } ], "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MembershipListResponse" - } - } - }, - "description": "" + "204": { + "description": "No response body" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -39841,7 +40625,7 @@ }, "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -39851,7 +40635,7 @@ }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -39861,7 +40645,7 @@ }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -39871,7 +40655,7 @@ }, "description": "" }, - "409": { + "429": { "content": { "application/json": { "schema": { @@ -39881,7 +40665,7 @@ }, "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -39891,11 +40675,12 @@ }, "description": "" }, - "503": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, @@ -39904,95 +40689,66 @@ } } }, - "/api/v1/admin/memberships/{id}": { - "delete": { - "operationId": "api_v1_admin_memberships_destroy", - "description": "Remove a single makerspace membership (un-assign a delegable role).\n\nScope contract (mirrors the create path's non-escalation model): a Space Manager may\nrevoke ONLY delegable-role memberships within their MANAGE_MAKERSPACE scope; a superadmin\nmay revoke any, except inside a superadmin-hidden makerspace (governance hard-block ->\n404). 404-before-403: out-of-scope existence is hidden as 404, a delegable-scope actor\naiming at a SPACE_MANAGER gets 403.", - "summary": "Revoke a makerspace staff membership", + "/api/v1/admin/makerspaces/{makerspace_id}/maintenance/feed/{raw_token}.ics": { + "get": { + "operationId": "api_v1_admin_makerspaces_maintenance_feed_.ics_retrieve", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true - } - ], - "tags": [ - "Admin users" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "Membership revoked." - }, - "403": { - "description": "A Space Manager membership can only be revoked by a superadmin." }, - "404": { - "description": "Membership not found or outside the actor's makerspace scope." - } - } - } - }, - "/api/v1/admin/memberships/{id}/capabilities": { - "patch": { - "operationId": "api_v1_admin_memberships_capabilities_partial_update", - "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", - "parameters": [ { "in": "path", - "name": "id", + "name": "raw_token", "schema": { - "type": "integer" + "type": "string" }, "required": true } ], "tags": [ - "Admin memberships" + "api" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedMembershipCapabilities" + "responses": { + "200": { + "content": { + "text/calendar": { + "schema": { + "type": "string", + "format": "binary" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedMembershipCapabilities" + "description": "" + }, + "304": { + "description": "No response body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedMembershipCapabilities" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminMembership" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "404": { "content": { "application/json": { "schema": { @@ -40002,7 +40758,7 @@ }, "description": "" }, - "401": { + "423": { "content": { "application/json": { "schema": { @@ -40012,7 +40768,7 @@ }, "description": "" }, - "403": { + "429": { "content": { "application/json": { "schema": { @@ -40022,7 +40778,7 @@ }, "description": "" }, - "404": { + "503": { "content": { "application/json": { "schema": { @@ -40032,11 +40788,12 @@ }, "description": "" }, - "409": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true } } }, @@ -40045,14 +40802,14 @@ } } }, - "/api/v1/admin/memberships/{id}/revoke": { - "post": { - "operationId": "api_v1_admin_memberships_revoke_create", - "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", + "/api/v1/admin/makerspaces/{makerspace_id}/member-card-template": { + "get": { + "operationId": "api_v1_admin_makerspaces_member_card_template_retrieve", + "summary": "Read the card layout template", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -40062,25 +40819,6 @@ "tags": [ "Admin memberships" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Revoke" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/Revoke" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/Revoke" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -40091,7 +40829,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminMembership" + "$ref": "#/components/schemas/MemberCardTemplate" } } }, @@ -40105,7 +40843,7 @@ } } }, - "description": "" + "description": "Invalid card input or membership module disabled." }, "401": { "content": { @@ -40115,7 +40853,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -40125,7 +40863,7 @@ } } }, - "description": "" + "description": "Missing member-card action or inactive account." }, "404": { "content": { @@ -40135,49 +40873,17 @@ } } }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found in the permitted makerspace." } } - } - }, - "/api/v1/admin/memberships/{id}/role": { - "patch": { - "operationId": "api_v1_admin_memberships_role_partial_update", - "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", + }, + "put": { + "operationId": "api_v1_admin_makerspaces_member_card_template_update", + "summary": "Replace the card layout template", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -40191,17 +40897,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedRoleId" + "$ref": "#/components/schemas/MemberCardTemplate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedRoleId" + "$ref": "#/components/schemas/MemberCardTemplate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedRoleId" + "$ref": "#/components/schemas/MemberCardTemplate" } } } @@ -40216,7 +40922,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminMembership" + "$ref": "#/components/schemas/MemberCardTemplate" } } }, @@ -40230,7 +40936,7 @@ } } }, - "description": "" + "description": "Invalid card input or membership module disabled." }, "401": { "content": { @@ -40240,7 +40946,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -40250,7 +40956,7 @@ } } }, - "description": "" + "description": "Missing member-card action or inactive account." }, "404": { "content": { @@ -40260,54 +40966,31 @@ } } }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found in the permitted makerspace." } } } }, - "/api/v1/admin/memberships/{id}/terms": { + "/api/v1/admin/makerspaces/{makerspace_id}/member-cards": { "get": { - "operationId": "api_v1_admin_memberships_terms_list", - "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", - "summary": "List the terms of a membership", + "operationId": "api_v1_admin_makerspaces_member_cards_list", + "summary": "List member cards", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string" + }, + "description": "active | revoked" } ], "tags": [ @@ -40325,7 +41008,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/components/schemas/MembershipTerm" + "$ref": "#/components/schemas/MemberCard" } } } @@ -40340,7 +41023,7 @@ } } }, - "description": "" + "description": "Invalid card input or membership module disabled." }, "401": { "content": { @@ -40350,7 +41033,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -40360,7 +41043,7 @@ } } }, - "description": "" + "description": "Missing member-card action or inactive account." }, "404": { "content": { @@ -40370,28 +41053,19 @@ } } }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found in the permitted makerspace." } } - }, + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/member-cards.pdf": { "post": { - "operationId": "api_v1_admin_memberships_terms_create", - "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", - "summary": "Open a term for a membership", + "operationId": "api_v1_admin_makerspaces_member_cards.pdf_create", + "summary": "Print a sheet of cards", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -40405,21 +41079,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipTermCreate" + "$ref": "#/components/schemas/MemberCardPrint" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MembershipTermCreate" + "$ref": "#/components/schemas/MemberCardPrint" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MembershipTermCreate" + "$ref": "#/components/schemas/MemberCardPrint" } } - }, - "required": true + } }, "security": [ { @@ -40427,11 +41100,12 @@ } ], "responses": { - "201": { + "200": { "content": { - "application/json": { + "application/pdf": { "schema": { - "$ref": "#/components/schemas/MembershipTerm" + "type": "string", + "format": "binary" } } }, @@ -40445,7 +41119,7 @@ } } }, - "description": "" + "description": "Invalid card input or membership module disabled." }, "401": { "content": { @@ -40455,7 +41129,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -40465,7 +41139,7 @@ } } }, - "description": "" + "description": "Missing member-card action or inactive account." }, "404": { "content": { @@ -40475,29 +41149,27 @@ } } }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found in the permitted makerspace." } } } }, - "/api/v1/admin/memberships/{id}/unverify": { + "/api/v1/admin/makerspaces/{makerspace_id}/member-cards/{membership_id}/issue": { "post": { - "operationId": "api_v1_admin_memberships_unverify_create", - "description": "Requires manage_members or an active membership with can_verify in this makerspace. Delegated verifiers cannot unverify themselves.", + "operationId": "api_v1_admin_makerspaces_member_cards_issue_create", + "summary": "Issue a card to a membership", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "membership_id", "schema": { "type": "integer" }, @@ -40507,17 +41179,36 @@ "tags": [ "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCardIssue" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberCardIssue" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberCardIssue" + } + } + } + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminMembership" + "$ref": "#/components/schemas/MemberCard" } } }, @@ -40531,7 +41222,7 @@ } } }, - "description": "" + "description": "Invalid card input or membership module disabled." }, "401": { "content": { @@ -40541,7 +41232,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -40551,7 +41242,7 @@ } } }, - "description": "" + "description": "Missing member-card action or inactive account." }, "404": { "content": { @@ -40561,29 +41252,19 @@ } } }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found in the permitted makerspace." } } } }, - "/api/v1/admin/memberships/{id}/verify": { + "/api/v1/admin/makerspaces/{makerspace_id}/member-cards/resolve": { "post": { - "operationId": "api_v1_admin_memberships_verify_create", - "description": "Requires manage_members or an active membership with can_verify in this makerspace. Delegated verifiers cannot verify themselves.", + "operationId": "api_v1_admin_makerspaces_member_cards_resolve_create", + "summary": "Resolve a scanned member card to minimal identity", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -40593,6 +41274,26 @@ "tags": [ "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCardResolve" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberCardResolve" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberCardResolve" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -40603,7 +41304,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminMembership" + "$ref": "#/components/schemas/MemberCardResolveResult" } } }, @@ -40617,7 +41318,7 @@ } } }, - "description": "" + "description": "Invalid card input or membership module disabled." }, "401": { "content": { @@ -40627,7 +41328,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -40637,7 +41338,7 @@ } } }, - "description": "" + "description": "Missing member-card action or inactive account." }, "404": { "content": { @@ -40647,28 +41348,19 @@ } } }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found in the permitted makerspace." } } } }, - "/api/v1/admin/memberships/{id}/waiver/witness": { - "post": { - "operationId": "api_v1_admin_memberships_waiver_witness_create", + "/api/v1/admin/makerspaces/{makerspace_id}/member-claim-codes": { + "get": { + "operationId": "api_v1_admin_makerspaces_member_claim_codes_list", + "summary": "List active physically handed member claim codes", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -40688,7 +41380,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WitnessWaiverResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberClaimCode" + } } } }, @@ -40745,16 +41440,14 @@ "description": "" } } - } - }, - "/api/v1/admin/organization-invitations/{id}/": { - "delete": { - "operationId": "api_v1_admin_organization_invitations_destroy", - "summary": "Revoke an unused organization invitation", + }, + "post": { + "operationId": "api_v1_admin_makerspaces_member_claim_codes_create", + "summary": "Issue a claim code to an eligible walk-in member", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -40762,28 +41455,45 @@ } ], "tags": [ - "Admin organizations" + "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberClaimCodeIssueRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberClaimCodeIssueRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberClaimCodeIssueRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "204": { - "description": "No response body" - }, - "401": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/MemberClaimCodeIssueResponse" } } }, - "description": "Authentication is required." + "description": "" }, - "403": { + "400": { "content": { "application/json": { "schema": { @@ -40791,9 +41501,9 @@ } } }, - "description": "Organization authority is required." + "description": "" }, - "404": { + "401": { "content": { "application/json": { "schema": { @@ -40801,12 +41511,9 @@ } } }, - "description": "Organization not found." - }, - "400": { - "description": "Invalid organization data." + "description": "" }, - "409": { + "403": { "content": { "application/json": { "schema": { @@ -40814,35 +41521,9 @@ } } }, - "description": "Organization state conflict." - } - } - } - }, - "/api/v1/admin/organizations/": { - "get": { - "operationId": "admin_organizations_list", - "summary": "List organizations visible to the actor", - "tags": [ - "Admin organizations" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OrganizationList" - } - } - }, "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { @@ -40850,9 +41531,9 @@ } } }, - "description": "Authentication is required." + "description": "" }, - "403": { + "409": { "content": { "application/json": { "schema": { @@ -40860,9 +41541,9 @@ } } }, - "description": "Organization authority is required." + "description": "" }, - "404": { + "429": { "content": { "application/json": { "schema": { @@ -40870,47 +41551,47 @@ } } }, - "description": "Organization not found." + "description": "Issue rate limit exceeded." } } - }, + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/member-claim-codes/{claim_id}/revoke": { "post": { - "operationId": "api_v1_admin_organizations_create", - "summary": "Create an organization (superadmin only)", - "tags": [ - "Admin organizations" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OrganizationCreate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/OrganizationCreate" - } + "operationId": "api_v1_admin_makerspaces_member_claim_codes_revoke_create", + "summary": "Revoke a member claim code and its bound session", + "parameters": [ + { + "in": "path", + "name": "claim_id", + "schema": { + "type": "integer" }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/OrganizationCreate" - } - } + "required": true }, - "required": true - }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin memberships" + ], "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationDetail" + "$ref": "#/components/schemas/MemberClaimCode" } } }, @@ -40920,193 +41601,71 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Validation failed, or the makerspace module this route belongs to is off." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not signed in." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "409": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Conflicts with the current state." + "description": "" } } } }, - "/api/v1/admin/organizations/{organization_id}/analytics/{report_key}": { + "/api/v1/admin/makerspaces/{makerspace_id}/membership-admission": { "get": { - "operationId": "api_v1_admin_organizations_analytics_retrieve", - "description": "Requires an active organization membership carrying the report's action. Authorization and the owned-makerspace set are resolved server-side; a combined total is always returned together with its per-makerspace breakdown.", - "summary": "Get organization analytics report", + "operationId": "api_v1_admin_makerspaces_membership_admission_retrieve", + "description": "Requires manage_makerspace; settings are separate from manage_members.", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, { "in": "path", - "name": "organization_id", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "report_key", - "schema": { - "type": "string", - "enum": [ - "active-loans", - "booking-utilization", - "damaged-lost", - "damaged-missing", - "event-attendance", - "evidence-compliance", - "fablab-health", - "machine-usage", - "maintenance-activity", - "member-activity", - "most-lent", - "payment-cash-book", - "payment-reconciliation", - "qr-scans", - "recently-added", - "returns", - "summary", - "taken-items", - "top-borrowers" - ] - }, - "required": true - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Analytics" + "Admin memberships" ], "security": [ { @@ -41118,7 +41677,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationReportResponse" + "$ref": "#/components/schemas/AdmissionSettings" } } }, @@ -41128,79 +41687,43 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid or excluded organization report." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Organization or report not found." - } - } - } - }, - "/api/v1/admin/organizations/{id}/": { - "get": { - "operationId": "admin_organizations_retrieve", - "summary": "Retrieve organization governance details", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin organizations" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OrganizationDetail" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "401": { + "409": { "content": { "application/json": { "schema": { @@ -41208,9 +41731,9 @@ } } }, - "description": "Authentication is required." + "description": "" }, - "403": { + "423": { "content": { "application/json": { "schema": { @@ -41218,9 +41741,9 @@ } } }, - "description": "Organization authority is required." + "description": "" }, - "404": { + "503": { "content": { "application/json": { "schema": { @@ -41228,17 +41751,17 @@ } } }, - "description": "Organization not found." + "description": "" } } }, "patch": { - "operationId": "api_v1_admin_organizations_partial_update", - "summary": "Update an organization public profile", + "operationId": "api_v1_admin_makerspaces_membership_admission_partial_update", + "description": "Requires manage_makerspace. Paid admissions require charge tracking, not an online gateway.", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -41246,23 +41769,23 @@ } ], "tags": [ - "Admin organizations" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedOrganizationProfileUpdate" + "$ref": "#/components/schemas/PatchedAdmissionSettings" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedOrganizationProfileUpdate" + "$ref": "#/components/schemas/PatchedAdmissionSettings" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedOrganizationProfileUpdate" + "$ref": "#/components/schemas/PatchedAdmissionSettings" } } } @@ -41277,7 +41800,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationDetail" + "$ref": "#/components/schemas/AdmissionSettings" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -41291,7 +41824,7 @@ } } }, - "description": "Authentication is required." + "description": "" }, "403": { "content": { @@ -41301,7 +41834,7 @@ } } }, - "description": "Organization authority is required." + "description": "" }, "404": { "content": { @@ -41311,10 +41844,7 @@ } } }, - "description": "Organization not found." - }, - "400": { - "description": "Invalid organization data." + "description": "" }, "409": { "content": { @@ -41324,19 +41854,40 @@ } } }, - "description": "Organization state conflict." + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/organizations/{id}/invitations/": { + "/api/v1/admin/makerspaces/{makerspace_id}/membership-plans": { "get": { - "operationId": "api_v1_admin_organizations_invitations_retrieve", - "summary": "List organization invitations without bearer tokens", + "operationId": "api_v1_admin_makerspaces_membership_plans_list", + "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", + "summary": "List membership plans", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -41344,7 +41895,7 @@ } ], "tags": [ - "Admin organizations" + "Admin memberships" ], "security": [ { @@ -41356,7 +41907,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationList" + "type": "array", + "items": { + "$ref": "#/components/schemas/MembershipPlan" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -41370,7 +41934,7 @@ } } }, - "description": "Authentication is required." + "description": "" }, "403": { "content": { @@ -41380,7 +41944,7 @@ } } }, - "description": "Organization authority is required." + "description": "" }, "404": { "content": { @@ -41390,17 +41954,28 @@ } } }, - "description": "Organization not found." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } }, "post": { - "operationId": "api_v1_admin_organizations_invitations_create", - "summary": "Create a single-use organization invitation", + "operationId": "api_v1_admin_makerspaces_membership_plans_create", + "description": "Requires manage_makerspace in this makerspace; manage_members alone cannot change plan definitions or prices.", + "summary": "Create a membership plan", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -41408,26 +41983,27 @@ } ], "tags": [ - "Admin organizations" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationCreate" + "$ref": "#/components/schemas/MembershipPlan" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationCreate" + "$ref": "#/components/schemas/MembershipPlan" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationCreate" + "$ref": "#/components/schemas/MembershipPlan" } } - } + }, + "required": true }, "security": [ { @@ -41439,13 +42015,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationCreated" + "$ref": "#/components/schemas/MembershipPlan" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -41453,9 +42029,9 @@ } } }, - "description": "Authentication is required." + "description": "" }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -41463,9 +42039,9 @@ } } }, - "description": "Organization authority is required." + "description": "" }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -41473,12 +42049,9 @@ } } }, - "description": "Organization not found." - }, - "400": { - "description": "Invalid organization data." + "description": "" }, - "409": { + "404": { "content": { "application/json": { "schema": { @@ -41486,18 +42059,29 @@ } } }, - "description": "Organization state conflict." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/organizations/{id}/makerspaces/": { + "/api/v1/admin/makerspaces/{makerspace_id}/memberships": { "get": { - "operationId": "api_v1_admin_organizations_makerspaces_retrieve", + "operationId": "api_v1_admin_makerspaces_memberships_list", + "description": "Requires manage_members in this makerspace. Staff creation and role administration require manage_makerspace.", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -41505,7 +42089,7 @@ } ], "tags": [ - "Admin organizations" + "Admin memberships" ], "security": [ { @@ -41517,7 +42101,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationLinkPage" + "type": "array", + "items": { + "$ref": "#/components/schemas/MembershipList" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -41527,76 +42124,51 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not signed in." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } }, "post": { - "operationId": "api_v1_admin_organizations_makerspaces_create", + "operationId": "api_v1_admin_makerspaces_memberships_create", + "description": "Requires manage_makerspace. Create or reattach staff with exactly one of role_id or role_ids (the complete set). Space Manager and Member are exclusive; unknown or foreign IDs return 404.", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -41604,23 +42176,23 @@ } ], "tags": [ - "Admin organizations" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationLinkWrite" + "$ref": "#/components/schemas/MembershipCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/OrganizationLinkWrite" + "$ref": "#/components/schemas/MembershipCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/OrganizationLinkWrite" + "$ref": "#/components/schemas/MembershipCreate" } } }, @@ -41632,11 +42204,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationLink" + "$ref": "#/components/schemas/MembershipList" } } }, @@ -41647,165 +42219,63 @@ "application/json": { "schema": { "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "additionalProperties": {} } } }, - "description": "Validation failed, or the makerspace module this route belongs to is off." + "description": "Field validation errors. Provide exactly one of role_id or role_ids; the set must be nonempty, and Space Manager and Member are exclusive." }, "401": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not signed in." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "description": "" }, "409": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } - } - } - }, - "description": "Conflicts with the current state." - }, - "423": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } - } - } - }, - "description": "Makerspace writes are frozen for tenant migration." - }, - "503": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "A required dependency is unavailable." + "description": "" } } } }, - "/api/v1/admin/organizations/{id}/makerspaces/{makerspace_id}/": { - "delete": { - "operationId": "api_v1_admin_organizations_makerspaces_destroy", + "/api/v1/admin/makerspaces/{makerspace_id}/memberships/{membership_id}/machine-scope": { + "get": { + "operationId": "api_v1_admin_makerspaces_memberships_machine_scope_retrieve", + "description": "Requires manage_makerspace in this makerspace.", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -41813,7 +42283,7 @@ }, { "in": "path", - "name": "makerspace_id", + "name": "membership_id", "schema": { "type": "integer" }, @@ -41821,7 +42291,7 @@ } ], "tags": [ - "Admin organizations" + "Admin memberships" ], "security": [ { @@ -41829,174 +42299,83 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } - } - } - }, - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { + "200": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/MemberMachineScope" } } }, - "description": "Not signed in." + "description": "" }, - "403": { + "400": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "" }, - "404": { + "401": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "description": "" }, - "409": { + "403": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Conflicts with the current state." + "description": "" }, - "423": { + "404": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace writes are frozen for tenant migration." + "description": "" }, - "503": { + "409": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true, - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" - }, - "purpose": { - "type": "string" - } - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "A required dependency is unavailable." + "description": "" } } - } - }, - "/api/v1/admin/organizations/{id}/memberships/": { - "get": { - "operationId": "api_v1_admin_organizations_memberships_retrieve", - "summary": "List organization memberships", + }, + "put": { + "operationId": "api_v1_admin_makerspaces_memberships_machine_scope_update", + "description": "Requires manage_makerspace. Replaces the member's complete type scope; exempt or non-Machine-Manager memberships return 409.", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "membership_id", "schema": { "type": "integer" }, @@ -42004,8 +42383,28 @@ } ], "tags": [ - "Admin organizations" + "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberMachineScopeWrite" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberMachineScopeWrite" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberMachineScopeWrite" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -42016,7 +42415,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationMembershipList" + "$ref": "#/components/schemas/MemberMachineScope" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -42030,7 +42439,7 @@ } } }, - "description": "Authentication is required." + "description": "" }, "403": { "content": { @@ -42040,7 +42449,7 @@ } } }, - "description": "Organization authority is required." + "description": "" }, "404": { "content": { @@ -42050,19 +42459,65 @@ } } }, - "description": "Organization not found." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/organized-events/": { - "get": { - "operationId": "api_v1_admin_organized_events_retrieve", - "description": "List the events this actor's organizations organize, across venues.\n\nOrganizer authority is deliberately per-event and grants nothing over the venue, so an\norganizer at a venue their organization is not linked to cannot use the venue's event\nlist -- which left the per-event endpoints reachable only by someone who already knew a\ndatabase id. This is the discoverable surface for that authority: it lists exactly the\nevents the organizer predicate matches and confers no venue authority whatsoever.", - "summary": "List events organized by the actor's organizations", + "/api/v1/admin/makerspaces/{makerspace_id}/memberships/{membership_id}/role": { + "patch": { + "operationId": "api_v1_admin_makerspaces_memberships_role_partial_update", + "description": "Requires manage_makerspace. Replace the complete role set using exactly one of role_id or role_ids. Duplicate IDs are ignored; Space Manager and Member are exclusive. Unknown or foreign IDs return 404.", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "membership_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Events" + "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedMembershipRoleAssign" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedMembershipRoleAssign" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedMembershipRoleAssign" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -42073,12 +42528,23 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EventListResponse" + "$ref": "#/components/schemas/MembershipList" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Field validation errors. Provide exactly one of role_id or role_ids; the set must be nonempty, and Space Manager and Member are exclusive." + }, "401": { "content": { "application/json": { @@ -42108,26 +42574,45 @@ } }, "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/platform/backup-destinations": { + "/api/v1/admin/makerspaces/{makerspace_id}/report-schedules": { "get": { - "operationId": "api_v1_admin_platform_backup_destinations_retrieve", - "summary": "List offsite backup destinations", + "operationId": "api_v1_admin_makerspaces_report_schedules_list", + "summary": "List report schedules", "parameters": [ { - "in": "query", - "name": "page", + "in": "path", + "name": "makerspace_id", "schema": { "type": "integer" }, - "description": "Page number (24 rows per page)." + "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } } ], "tags": [ - "Backup destinations" + "Reports" ], "security": [ { @@ -42139,128 +42624,81 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BackupDestinationPage" + "$ref": "#/components/schemas/PaginatedReportScheduleList" } } }, "description": "" }, - "404": { - "description": "The requested resource does not exist in the actor's scope." + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid schedule or module disabled." }, "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "The authenticated actor is not authorized." - } - } - }, - "post": { - "operationId": "api_v1_admin_platform_backup_destinations_create", - "summary": "Register an S3-compatible offsite backup destination", - "tags": [ - "Backup destinations" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BackupDestination" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/BackupDestination" + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/BackupDestination" - } - } + "description": "Makerspace or schedule not found." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BackupDestination" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" - }, - "400": { - "description": "The request is invalid for the current lifecycle state." - }, - "401": { - "description": "Authentication is required." + "description": "Tenant writes are frozen." }, - "403": { - "description": "The authenticated actor is not authorized." - } - } - } - }, - "/api/v1/admin/platform/backup-destinations/{destination_id}": { - "get": { - "operationId": "api_v1_admin_platform_backup_destinations_retrieve_2", - "summary": "Get one offsite backup destination", - "parameters": [ - { - "in": "path", - "name": "destination_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Backup destinations" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BackupDestination" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" - }, - "404": { - "description": "The requested resource does not exist in the actor's scope." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "The authenticated actor is not authorized." + "description": "Tenant write gate unavailable." } } }, - "patch": { - "operationId": "api_v1_admin_platform_backup_destinations_partial_update", - "summary": "Update or rotate credentials of an offsite destination", + "post": { + "operationId": "api_v1_admin_makerspaces_report_schedules_create", + "summary": "Create report schedule", "parameters": [ { "in": "path", - "name": "destination_id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -42268,26 +42706,27 @@ } ], "tags": [ - "Backup destinations" + "Reports" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedBackupDestination" + "$ref": "#/components/schemas/ReportSchedule" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedBackupDestination" + "$ref": "#/components/schemas/ReportSchedule" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedBackupDestination" + "$ref": "#/components/schemas/ReportSchedule" } } - } + }, + "required": true }, "security": [ { @@ -42295,39 +42734,87 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BackupDestination" + "$ref": "#/components/schemas/ReportSchedule" } } }, "description": "" }, "400": { - "description": "The request is invalid for the current lifecycle state." - }, - "404": { - "description": "The requested resource does not exist in the actor's scope." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid schedule or module disabled." }, "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "The authenticated actor is not authorized." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or schedule not found." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Tenant writes are frozen." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Tenant write gate unavailable." } } } }, - "/api/v1/admin/platform/backup-destinations/{destination_id}/retire": { - "post": { - "operationId": "api_v1_admin_platform_backup_destinations_retire_create", - "summary": "Retire a destination: stop pushing and wipe its stored credentials", + "/api/v1/admin/makerspaces/{makerspace_id}/roles": { + "get": { + "operationId": "api_v1_admin_makerspaces_roles_list", + "description": "Requires manage_makerspace for all roles, or manage_members for action-less community roles only.", "parameters": [ { "in": "path", - "name": "destination_id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -42335,7 +42822,7 @@ } ], "tags": [ - "Backup destinations" + "Admin roles" ], "security": [ { @@ -42347,80 +42834,75 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BackupDestination" + "type": "array", + "items": { + "$ref": "#/components/schemas/Role" + } } } }, "description": "" }, "400": { - "description": "The request is invalid for the current lifecycle state." - }, - "404": { - "description": "The requested resource does not exist in the actor's scope." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "The authenticated actor is not authorized." - } - } - } - }, - "/api/v1/admin/platform/backup-destinations/{destination_id}/test": { - "post": { - "operationId": "api_v1_admin_platform_backup_destinations_test_create", - "summary": "Probe write, read-back and delete on a destination", - "parameters": [ - { - "in": "path", - "name": "destination_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Backup destinations" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DestinationProbe" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, "404": { - "description": "The requested resource does not exist in the actor's scope." - }, - "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "403": { - "description": "The authenticated actor is not authorized." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/platform/backup-destinations/{destination_id}/uploads": { + "/api/v1/admin/makerspaces/{makerspace_id}/saved-report-views": { "get": { - "operationId": "api_v1_admin_platform_backup_destinations_uploads_retrieve", - "summary": "List the upload journal of one destination", + "operationId": "api_v1_admin_makerspaces_saved_report_views_retrieve", "parameters": [ { "in": "path", - "name": "destination_id", + "name": "makerspace_id", "schema": { "type": "integer" }, @@ -42431,12 +42913,11 @@ "name": "page", "schema": { "type": "integer" - }, - "description": "Page number (24 rows per page)." + } } ], "tags": [ - "Backup destinations" + "Reports" ], "security": [ { @@ -42448,114 +42929,108 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BackupDestinationUploadPage" + "$ref": "#/components/schemas/SavedReportViewList" } } }, "description": "" }, - "404": { - "description": "The requested resource does not exist in the actor's scope." + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, "403": { - "description": "The authenticated actor is not authorized." - } - } - } - }, - "/api/v1/admin/platform/backup-destinations/push": { - "post": { - "operationId": "api_v1_admin_platform_backup_destinations_push_create", - "summary": "Start an offsite push now instead of waiting for the schedule", - "tags": [ - "Backup destinations" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DestinationDispatch" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, - "503": { - "description": "The backup worker is unavailable." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "The authenticated actor is not authorized." - } - } - } - }, - "/api/v1/admin/platform/backup-settings": { - "get": { - "operationId": "api_v1_admin_platform_backup_settings_retrieve", - "summary": "Get deployment backup settings", - "tags": [ - "Backup" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformBackupSettings" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, - "401": { - "description": "Authentication is required." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, - "403": { - "description": "The authenticated actor is not authorized." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" } } }, - "patch": { - "operationId": "api_v1_admin_platform_backup_settings_partial_update", - "summary": "Update deployment backup settings", + "post": { + "operationId": "api_v1_admin_makerspaces_saved_report_views_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Backup" + "Reports" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformBackupSettings" + "$ref": "#/components/schemas/SavedReportView" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformBackupSettings" + "$ref": "#/components/schemas/SavedReportView" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformBackupSettings" + "$ref": "#/components/schemas/SavedReportView" } } - } + }, + "required": true }, "security": [ { @@ -42563,342 +43038,219 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformBackupSettings" + "$ref": "#/components/schemas/SavedReportView" } } }, "description": "" }, "400": { - "description": "The request is invalid for the current lifecycle state." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "The authenticated actor is not authorized." - } - } - } - }, - "/api/v1/admin/platform/backups": { - "get": { - "operationId": "api_v1_admin_platform_backups_list", - "summary": "List full-deployment backup archives", - "tags": [ - "Backup" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BackupArchive" - } + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, "403": { - "description": "The authenticated actor is not authorized." - } - } - }, - "post": { - "operationId": "api_v1_admin_platform_backups_create", - "summary": "Request an age-encrypted full-deployment backup", - "tags": [ - "Backup" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BackupArchive" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, - "503": { - "description": "The backup worker is unavailable." - }, - "401": { - "description": "Authentication is required." + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, - "403": { - "description": "The authenticated actor is not authorized." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/platform/backups/{archive_id}/rehydrate": { - "post": { - "operationId": "api_v1_admin_platform_backups_rehydrate_create", - "summary": "Restore an archive's primary bytes from a verified offsite copy", + "/api/v1/admin/makerspaces/{makerspace_id}/saved-report-views/{id}": { + "get": { + "operationId": "api_v1_admin_makerspaces_saved_report_views_retrieve_2", "parameters": [ { "in": "path", - "name": "archive_id", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, "required": true } ], "tags": [ - "Backup destinations" + "Reports" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Rehydrate" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/Rehydrate" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/Rehydrate" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "202": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DestinationDispatch" + "$ref": "#/components/schemas/SavedReportView" } } }, "description": "" }, "400": { - "description": "The request is invalid for the current lifecycle state." - }, - "404": { - "description": "The requested resource does not exist in the actor's scope." - }, - "503": { - "description": "The backup worker is unavailable." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "The authenticated actor is not authorized." - } - } - } - }, - "/api/v1/admin/platform/dashboard": { - "get": { - "operationId": "api_v1_admin_platform_dashboard_retrieve", - "summary": "Read platform operations dashboard", - "tags": [ - "Platform" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformDashboard" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - } - } - } - }, - "/api/v1/admin/platform/email-settings": { - "get": { - "operationId": "api_v1_admin_platform_email_settings_retrieve", - "summary": "Retrieve or update platform email settings", - "tags": [ - "Platform" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformEmailSettings" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, - "401": { - "description": "Not signed in." - }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - } - } - }, - "patch": { - "operationId": "api_v1_admin_platform_email_settings_partial_update", - "summary": "Retrieve or update platform email settings", - "tags": [ - "Platform" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedPlatformEmailSettings" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedPlatformEmailSettings" + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedPlatformEmailSettings" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformEmailSettings" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - } - } - } - }, - "/api/v1/admin/platform/payment-settings": { - "get": { - "operationId": "api_v1_admin_platform_payment_settings_retrieve", - "summary": "Retrieve or update platform Stripe Connect settings", - "tags": [ - "Platform" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformStripeConnectSettings" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Self-hosted deployment: Stripe Connect is a managed-only surface." } } }, "patch": { - "operationId": "api_v1_admin_platform_payment_settings_partial_update", - "summary": "Retrieve or update platform Stripe Connect settings", + "operationId": "api_v1_admin_makerspaces_saved_report_views_partial_update", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Platform" + "Reports" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformStripeConnectSettings" + "$ref": "#/components/schemas/PatchedSavedReportView" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformStripeConnectSettings" + "$ref": "#/components/schemas/PatchedSavedReportView" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformStripeConnectSettings" + "$ref": "#/components/schemas/PatchedSavedReportView" } } } @@ -42913,68 +43265,96 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformStripeConnectSettings" + "$ref": "#/components/schemas/SavedReportView" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, "404": { - "description": "Self-hosted deployment: Stripe Connect is a managed-only surface." - } - } - } - }, - "/api/v1/admin/platform/problems": { - "get": { - "operationId": "api_v1_admin_platform_problems_retrieve", - "summary": "Run read-only platform problem checks on demand", - "tags": [ - "Platform" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformProblems" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" } } - } - }, - "/api/v1/admin/platform/restores": { - "get": { - "operationId": "api_v1_admin_platform_restores_list", - "summary": "List deployment restore operations", + }, + "delete": { + "operationId": "api_v1_admin_makerspaces_saved_report_views_destroy", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Backup" + "Reports" ], "security": [ { @@ -42982,98 +43362,111 @@ } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "400": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RestoreOperation" - } + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" }, "403": { - "description": "The authenticated actor is not authorized." - } - } - }, - "post": { - "operationId": "api_v1_admin_platform_restores_create", - "summary": "Record restore intent for the privileged host supervisor", - "tags": [ - "Backup" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestoreCreate" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/RestoreCreate" + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RestoreCreate" - } - } + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "202": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RestoreOperation" + "$ref": "#/components/schemas/ReportError" } } }, "description": "" }, - "400": { - "description": "The request is invalid for the current lifecycle state." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "The authenticated actor is not authorized." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/platform/restores/{restore_id}": { + "/api/v1/admin/makerspaces/{makerspace_id}/saved-views": { "get": { - "operationId": "api_v1_admin_platform_restores_retrieve", - "summary": "Get restore stage, diff, and decision deadline", + "operationId": "api_v1_admin_makerspaces_saved_views_retrieve", "parameters": [ { - "in": "path", - "name": "restore_id", + "in": "query", + "name": "entity", "schema": { "type": "string", - "format": "uuid" + "enum": [ + "audit", + "bookings", + "events", + "inventory", + "members", + "payments", + "requests" + ] + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } } ], "tags": [ - "Backup" + "Staff lists" ], "security": [ { @@ -43085,148 +43478,108 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RestoreOperation" + "$ref": "#/components/schemas/SavedViewList" } } }, "description": "" }, - "404": { - "description": "The requested resource does not exist in the actor's scope." + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "The authenticated actor is not authorized." - } - } - } - }, - "/api/v1/admin/platform/restores/{restore_id}/decision": { - "post": { - "operationId": "api_v1_admin_platform_restores_decision_create", - "summary": "Decide a quiesced in-place restore", - "parameters": [ - { - "in": "path", - "name": "restore_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Backup" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RestoreDecision" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/RestoreDecision" + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RestoreDecision" - } - } + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RestoreOperation" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "The request is invalid for the current lifecycle state." - }, - "404": { - "description": "The requested resource does not exist in the actor's scope." - }, - "401": { - "description": "Authentication is required." - }, - "403": { - "description": "The authenticated actor is not authorized." - } - } - } - }, - "/api/v1/admin/platform/social-auth-settings": { - "get": { - "operationId": "api_v1_admin_platform_social_auth_settings_retrieve", - "summary": "Retrieve or update platform social auth settings", - "tags": [ - "Platform" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformSocialAuthSettings" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." } } }, - "patch": { - "operationId": "api_v1_admin_platform_social_auth_settings_partial_update", - "summary": "Retrieve or update platform social auth settings", + "post": { + "operationId": "api_v1_admin_makerspaces_saved_views_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Platform" + "Staff lists" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformSocialAuthSettings" + "$ref": "#/components/schemas/SavedView" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformSocialAuthSettings" + "$ref": "#/components/schemas/SavedView" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedPlatformSocialAuthSettings" + "$ref": "#/components/schemas/SavedView" } } - } + }, + "required": true }, "security": [ { @@ -43234,110 +43587,102 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PlatformSocialAuthSettings" + "$ref": "#/components/schemas/SavedView" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - } - } - } - }, - "/api/v1/admin/platform/tenant-migrations/deployment-identity": { - "get": { - "operationId": "api_v1_admin_platform_tenant_migrations_deployment_identity_retrieve", - "summary": "Read this deployment's signing identity and target age recipient", - "tags": [ - "Tenant migration" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeploymentIdentity" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "State conflict." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "" } } } }, - "/api/v1/admin/platform/tenant-migrations/imports": { + "/api/v1/admin/makerspaces/{makerspace_id}/saved-views/{id}": { "get": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_list", - "summary": "List tenant import jobs", + "operationId": "api_v1_admin_makerspaces_saved_views_retrieve_2", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Tenant migration" + "Staff lists" ], "security": [ { @@ -43349,10 +43694,17 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImportJob" - } + "$ref": "#/components/schemas/SavedView" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -43362,69 +43714,95 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } }, - "post": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_create", - "summary": "Create an import job from an age-encrypted archive upload", + "patch": { + "operationId": "api_v1_admin_makerspaces_saved_views_partial_update", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Tenant migration" + "Staff lists" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ImportCreate" + "$ref": "#/components/schemas/PatchedSavedView" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ImportCreate" + "$ref": "#/components/schemas/PatchedSavedView" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ImportCreate" + "$ref": "#/components/schemas/PatchedSavedView" } } - }, - "required": true + } }, "security": [ { @@ -43432,11 +43810,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ImportJob" + "$ref": "#/components/schemas/SavedView" } } }, @@ -43446,82 +43824,86 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FieldValidationError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Field-keyed validation errors." + "description": "" }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "State conflict." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "" } } - } - }, - "/api/v1/admin/platform/tenant-migrations/imports/{job_id}": { - "get": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_retrieve", - "summary": "Read a tenant import job", + }, + "delete": { + "operationId": "api_v1_admin_makerspaces_saved_views_destroy", "parameters": [ { "in": "path", - "name": "job_id", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, "required": true } ], "tags": [ - "Tenant migration" + "Staff lists" ], "security": [ { @@ -43529,86 +43911,112 @@ } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ImportJob" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "404": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "" } } } }, - "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/identity-decisions": { + "/api/v1/admin/makerspaces/{makerspace_id}/spaces/": { "get": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_identity_decisions_list", - "summary": "Read the exact archived identity decision list", + "operationId": "api_v1_admin_makerspaces_spaces_retrieve", + "summary": "List bookable spaces in a makerspace", "parameters": [ { "in": "path", - "name": "job_id", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "ordering", + "schema": { + "type": "string" + }, + "description": "Allowlisted sort field; prefix with '-' for descending." + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number, 24 rows per page." + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Search within the authorized list." } ], "tags": [ - "Tenant migration" + "Admin bookings" ], "security": [ { @@ -43620,30 +44028,27 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ClosureIdentity" - } + "$ref": "#/components/schemas/BookableSpaceListResponse" } } }, "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -43653,66 +44058,55 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "Permission denied." }, - "500": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "Not found." } } }, "post": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_identity_decisions_create", - "summary": "Submit all per-person import identity decisions", + "operationId": "api_v1_admin_makerspaces_spaces_create", + "summary": "Create a bookable space", "parameters": [ { "in": "path", - "name": "job_id", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Tenant migration" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ImportDecisionList" + "$ref": "#/components/schemas/BookableSpaceWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ImportDecisionList" + "$ref": "#/components/schemas/BookableSpaceWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ImportDecisionList" + "$ref": "#/components/schemas/BookableSpaceWrite" } } }, @@ -43724,11 +44118,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ImportJob" + "$ref": "#/components/schemas/BookableSpaceAdmin" } } }, @@ -43738,37 +44132,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FieldValidationError" - } - } - }, - "description": "Field-keyed validation errors." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "State conflict." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -43778,61 +44152,49 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "Permission denied." }, - "429": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." + "description": "Not found." } } } }, - "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/pairings/{pairing_id}/abort": { - "post": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_pairings_abort_create", - "summary": "Abort an importing target and issue its signed proof", + "/api/v1/admin/makerspaces/{makerspace_id}/staff-invitations": { + "get": { + "operationId": "api_v1_admin_makerspaces_staff_invitations_list", "parameters": [ { "in": "path", - "name": "job_id", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true }, { - "in": "path", - "name": "pairing_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "type": "integer" + } } ], "tags": [ - "Tenant migration" + "Staff invitations" ], "security": [ { @@ -43844,117 +44206,264 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CutoverOutcome" + "$ref": "#/components/schemas/PaginatedStaffInvitationList" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + } + } + }, + "post": { + "operationId": "api_v1_admin_makerspaces_staff_invitations_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Staff invitations" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaffInvitationCreate" } }, - "description": "Not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StaffInvitationCreate" } }, - "description": "State conflict." + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StaffInvitationCreate" + } + } }, - "401": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/StaffInvitationDelivery" } } }, - "description": "Authentication required." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/staff-invitations/{invitation_id}/resend": { + "post": { + "operationId": "api_v1_admin_makerspaces_staff_invitations_resend_create", + "parameters": [ + { + "in": "path", + "name": "invitation_id", + "schema": { + "type": "integer" }, - "description": "Throttle limit exceeded." + "required": true }, - "500": { + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Staff invitations" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/StaffInvitationDelivery" } } }, - "description": "Unexpected migration failure." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/pairings/{pairing_id}/activate": { + "/api/v1/admin/makerspaces/{makerspace_id}/staff-invitations/{invitation_id}/revoke": { "post": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_pairings_activate_create", - "summary": "Activate an imported target with the source receipt", + "operationId": "api_v1_admin_makerspaces_staff_invitations_revoke_create", "parameters": [ { "in": "path", - "name": "job_id", + "name": "invitation_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true }, { "in": "path", - "name": "pairing_id", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Tenant migration" + "Staff invitations" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaffInvitation" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/uploads/evidence-url": { + "post": { + "operationId": "api_v1_admin_makerspaces_uploads_evidence_url_create", + "description": "Base for ALL staff endpoints: authenticated + active staff + auto-scoped queryset.\n\nFuture phases subclass this so the invariant 'every staff query is makerspace-scoped'\nis enforced by default rather than by remembering to add a mixin (review fix #4). Add\n`required_action` + `HasMakerspaceAction` to a subclass for per-action checks.", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "api" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CutoverReceiptRequest" + "$ref": "#/components/schemas/EvidenceUrlRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CutoverReceiptRequest" + "$ref": "#/components/schemas/EvidenceUrlRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CutoverReceiptRequest" + "$ref": "#/components/schemas/EvidenceUrlRequest" } } }, @@ -43965,12 +44474,57 @@ "jwtAuth": [] } ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvidenceUrlResponse" + } + } + }, + "description": "" + }, + "400": { + "description": "Invalid evidence upload request." + }, + "403": { + "description": "Insufficient makerspace permission." + }, + "503": { + "description": "Evidence storage is unavailable." + } + } + } + }, + "/api/v1/admin/makerspaces/{makerspace_id}/waiver": { + "get": { + "operationId": "api_v1_admin_makerspaces_waiver_retrieve", + "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CutoverOutcome" + "$ref": "#/components/schemas/MemberWaiverResponse" } } }, @@ -43980,108 +44534,105 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FieldValidationError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Field-keyed validation errors." + "description": "" }, - "404": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found." + "description": "" }, - "409": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "State conflict." + "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "" } } - } - }, - "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/run": { - "post": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_run_create", - "summary": "Run an identity-decided tenant import", + }, + "put": { + "operationId": "api_v1_admin_makerspaces_waiver_update", + "description": "Requires manage_makerspace in this makerspace to publish or clear waiver text.", "parameters": [ { "in": "path", - "name": "job_id", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Tenant migration" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ImportRun" + "$ref": "#/components/schemas/WaiverPublish" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ImportRun" + "$ref": "#/components/schemas/WaiverPublish" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ImportRun" + "$ref": "#/components/schemas/WaiverPublish" } } } @@ -44092,11 +44643,11 @@ } ], "responses": { - "202": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ImportJob" + "$ref": "#/components/schemas/MemberWaiverResponse" } } }, @@ -44106,95 +44657,91 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FieldValidationError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Field-keyed validation errors." + "description": "" }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "State conflict." + "description": "" }, - "404": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found." - }, - "503": { - "description": "Import worker unavailable." + "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "" } } } }, - "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/verification": { + "/api/v1/admin/makerspaces/{makerspace_id}/walk-in-members": { "get": { - "operationId": "api_v1_admin_platform_tenant_migrations_imports_verification_retrieve", - "summary": "Read the import verification report", + "operationId": "api_v1_admin_makerspaces_walk_in_members_retrieve", + "summary": "Read walk-in admission questions", "parameters": [ { "in": "path", - "name": "job_id", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Tenant migration" + "Admin memberships" ], "security": [ { @@ -44206,164 +44753,115 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VerificationReport" + "$ref": "#/components/schemas/PublicAdmission" } } }, "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not found." + "description": "" }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "State conflict." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." - } - } - } - }, - "/api/v1/admin/platform/tenant-migrations/pairings": { - "get": { - "operationId": "api_v1_admin_platform_tenant_migrations_pairings_list", - "summary": "List pinned migration pairings", - "tags": [ - "Tenant migration" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "" + }, + "503": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Pairing" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Superadmin access required." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Throttle limit exceeded." - }, - "500": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TypedError" - } - } - }, - "description": "Unexpected migration failure." } } }, "post": { - "operationId": "api_v1_admin_platform_tenant_migrations_pairings_create", - "summary": "Approve and pin source/target deployment identities", + "operationId": "api_v1_admin_makerspaces_walk_in_members_create", + "summary": "Create a walk-in member record", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Tenant migration" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PairingCreate" + "$ref": "#/components/schemas/WalkInMemberCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PairingCreate" + "$ref": "#/components/schemas/WalkInMemberCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PairingCreate" + "$ref": "#/components/schemas/WalkInMemberCreate" } } }, @@ -44379,7 +44877,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Pairing" + "$ref": "#/components/schemas/WalkInAdmissionResult" } } }, @@ -44389,81 +44887,90 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FieldValidationError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Field-keyed validation errors." + "description": "" }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "State conflict." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Superadmin access required." + "description": "" }, - "429": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Throttle limit exceeded." + "description": "" }, - "500": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TypedError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Unexpected migration failure." + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/printer-jobs/{id}": { + "/api/v1/admin/makerspaces/{makerspace_id}/website-embed-origins/": { "get": { - "operationId": "api_v1_admin_printer_jobs_retrieve", + "operationId": "api_v1_admin_makerspaces_website_embed_origins_retrieve", "parameters": [ { "in": "path", - "name": "id", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Devices" + "Public websites" ], "security": [ { @@ -44475,101 +44982,154 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Job" + "$ref": "#/components/schemas/WebsiteOrigins" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid payload or adapter module disabled." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication/signature/replay refused." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authority or device revoked." + "description": "" + } + } + }, + "put": { + "operationId": "api_v1_admin_makerspaces_website_embed_origins_update", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Public websites" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteOrigins" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WebsiteOrigins" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WebsiteOrigins" + } + } }, - "404": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/WebsiteOrigins" } } }, - "description": "Target outside scope or absent." + "description": "" }, - "409": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Conflicting command/report." + "description": "" }, - "410": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Delivery lease expired." + "description": "" }, - "423": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/printer-jobs/{id}/apply": { - "post": { - "operationId": "api_v1_admin_printer_jobs_apply_create", + "/api/v1/admin/makerspaces/{id}": { + "get": { + "operationId": "api_v1_admin_makerspaces_retrieve", + "summary": "Retrieve a makerspace's settings", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Devices" + "Admin makerspaces" ], "security": [ { @@ -44581,89 +45141,209 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Job" + "$ref": "#/components/schemas/Makerspace" } } }, "description": "" }, - "400": { + "401": { + "description": "Authentication required." + }, + "404": { + "description": "Makerspace not found, archived, not active, hidden from superadmins, or outside the actor's scope for this method." + } + } + }, + "patch": { + "operationId": "api_v1_admin_makerspaces_partial_update", + "description": "Needs manage_makerspace. `enabled_modules` is superadmin-owned and edited only in /control/ or the module console: a body carrying it is refused with 403.", + "summary": "Update a makerspace's settings", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin makerspaces" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedMakerspace" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedMakerspace" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedMakerspace" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/Makerspace" } } }, - "description": "Invalid payload or adapter module disabled." + "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "type": "object", + "additionalProperties": {}, + "properties": { + "theme_config": { + "oneOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "object", + "required": [ + "code", + "messages" + ], + "properties": { + "code": { + "type": "string", + "enum": [ + "theme_contrast" + ] + }, + "messages": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + ] + } + } } } }, - "description": "Authentication/signature/replay refused." + "description": "Field validation errors. Contrast errors have theme_config: {code: 'theme_contrast', messages: string[]} with mode, failing pair, measured ratio and required ratio." + }, + "401": { + "description": "Authentication required." }, "403": { + "description": "The body carries `enabled_modules`." + }, + "404": { + "description": "Makerspace not found, archived, not active, hidden from superadmins, or outside the actor's scope for this method." + } + } + } + }, + "/api/v1/admin/member-cards/{id}/print.pdf": { + "post": { + "operationId": "api_v1_admin_member_cards_print.pdf_create", + "summary": "Print one card (CR80 PDF)", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { - "application/json": { + "application/pdf": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "type": "string", + "format": "binary" } } }, - "description": "Authority or device revoked." + "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Target outside scope or absent." + "description": "Invalid card input or membership module disabled." }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Conflicting command/report." + "description": "Authentication required." }, - "410": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Delivery lease expired." + "description": "Missing member-card action or inactive account." }, - "423": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Not found in the permitted makerspace." } } } }, - "/api/v1/admin/products/{id}/assets/generate": { + "/api/v1/admin/member-cards/{id}/reissue": { "post": { - "operationId": "api_v1_admin_products_assets_generate_create", - "summary": "Generate asset units", + "operationId": "api_v1_admin_member_cards_reissue_create", + "summary": "Reissue (rotate the QR of) a card", "parameters": [ { "in": "path", @@ -44675,23 +45355,23 @@ } ], "tags": [ - "Asset units" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AssetGenerate" + "$ref": "#/components/schemas/MemberCardReissue" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/AssetGenerate" + "$ref": "#/components/schemas/MemberCardReissue" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/AssetGenerate" + "$ref": "#/components/schemas/MemberCardReissue" } } }, @@ -44703,11 +45383,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AssetGenerateResult" + "$ref": "#/components/schemas/MemberCard" } } }, @@ -44717,39 +45397,17 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid input, disabled module, or non-draft print batch." + "description": "Invalid card input or membership module disabled." }, "401": { "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -44759,72 +45417,29 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "QR management permission required." + "description": "Missing member-card action or inactive account." }, "404": { "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } - } - }, - "description": "Batch or inventory target not in scope." - }, - "423": { - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Not found in the permitted makerspace." } } } }, - "/api/v1/admin/qr-print-batches/{id}": { - "get": { - "operationId": "api_v1_admin_qr_print_batches_retrieve", - "summary": "Retrieve QR print batch", + "/api/v1/admin/member-cards/{id}/revoke": { + "post": { + "operationId": "api_v1_admin_member_cards_revoke_create", + "summary": "Revoke a card (redacts name and photo)", "parameters": [ { "in": "path", @@ -44836,8 +45451,27 @@ } ], "tags": [ - "QR print batches" + "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCardRevoke" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberCardRevoke" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberCardRevoke" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -44848,56 +45482,60 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrPrintBatchDetail" + "$ref": "#/components/schemas/MemberCard" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/qr-print-batches/{id}/download": { - "get": { - "operationId": "api_v1_admin_qr_print_batches_download_retrieve", - "summary": "Download QR print batch as a ZIP of captioned SVGs", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "QR print batches" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "Invalid card input or membership module disabled." + }, + "401": { "content": { - "application/zip": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Missing member-card action or inactive account." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found in the permitted makerspace." } } } }, - "/api/v1/admin/qr-print-batches/{id}/items": { - "post": { - "operationId": "api_v1_admin_qr_print_batches_items_create", - "summary": "Add QR code to draft print batch", + "/api/v1/admin/membership-plans/{id}": { + "patch": { + "operationId": "api_v1_admin_membership_plans_partial_update", + "description": "Requires manage_makerspace in this makerspace; manage_members alone cannot change plan definitions or prices.", + "summary": "Update a membership plan", "parameters": [ { "in": "path", @@ -44909,27 +45547,26 @@ } ], "tags": [ - "QR print batches" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrPrintBatchItemCreate" + "$ref": "#/components/schemas/PatchedMembershipPlan" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/QrPrintBatchItemCreate" + "$ref": "#/components/schemas/PatchedMembershipPlan" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/QrPrintBatchItemCreate" + "$ref": "#/components/schemas/PatchedMembershipPlan" } } - }, - "required": true + } }, "security": [ { @@ -44937,11 +45574,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrPrintBatchItemResult" + "$ref": "#/components/schemas/MembershipPlan" } } }, @@ -44951,135 +45588,94 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid input, disabled module, or non-draft print batch." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "QR management permission required." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Batch or inventory target not in scope." + "description": "" }, - "423": { + "409": { "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "" } } } }, - "/api/v1/admin/qr-print-batches/{id}/label-commands": { + "/api/v1/admin/membership-requests": { "get": { - "operationId": "api_v1_admin_qr_print_batches_label_commands_list", - "summary": "List label commands for a QR batch", + "operationId": "api_v1_admin_membership_requests_retrieve", + "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", "parameters": [ { - "in": "path", - "name": "id", + "in": "query", + "name": "makerspace_id", "schema": { "type": "integer" }, "required": true }, { - "name": "page", - "required": false, "in": "query", - "description": "A page number within the paginated result set.", + "name": "ordering", + "schema": { + "type": "string" + }, + "description": "Allowlisted sort field; prefix with '-' for descending." + }, + { + "in": "query", + "name": "page", "schema": { "type": "integer" + }, + "description": "Page number, 24 rows per page." + }, + { + "in": "query", + "name": "state", + "schema": { + "type": "string" } } ], "tags": [ - "Devices" + "Admin memberships" ], "security": [ { @@ -45091,7 +45687,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedLabelCommandList" + "$ref": "#/components/schemas/MembershipRequestListResponse" } } }, @@ -45101,88 +45697,79 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "DRF field/module validation errors or non-field validation messages." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required or invalid signature." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Command or batch not in scope." + "description": "" }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Idempotency or delivery conflict." + "description": "" }, - "410": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Batch no longer available." + "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "" } } - }, + } + }, + "/api/v1/admin/membership-requests/{id}/approve": { "post": { - "operationId": "api_v1_admin_qr_print_batches_label_commands_create", - "summary": "Send or reprint a QR batch", + "operationId": "api_v1_admin_membership_requests_approve_create", + "description": "Requires manage_members. Returns an admission request when payment is due, otherwise the activated membership.", "parameters": [ { "in": "path", @@ -45194,23 +45781,23 @@ } ], "tags": [ - "Devices" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LabelCreate" + "$ref": "#/components/schemas/ApproveRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/LabelCreate" + "$ref": "#/components/schemas/ApproveRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/LabelCreate" + "$ref": "#/components/schemas/ApproveRequest" } } }, @@ -45226,17 +45813,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LabelCommand" - } - } - }, - "description": "" - }, - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LabelCommand" + "$ref": "#/components/schemas/MembershipApprovalOutcome" } } }, @@ -45246,119 +45823,79 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "DRF field/module validation errors or non-field validation messages." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required or invalid signature." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Command or batch not in scope." + "description": "" }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Idempotency or delivery conflict." + "description": "" }, - "410": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Batch no longer available." + "description": "" }, - "423": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." - } - } - } - }, - "/api/v1/admin/qr/{id}/print": { - "get": { - "operationId": "api_v1_admin_qr_print_retrieve", - "summary": "Render QR label SVG", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "QR assets" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "description": "SVG QR label." + "description": "" } } } }, - "/api/v1/admin/qr/{id}/rebind-target": { + "/api/v1/admin/membership-requests/{id}/revoke": { "post": { - "operationId": "api_v1_admin_qr_rebind_target_create", - "summary": "Rebind a saved QR to another product/asset (cross-makerspace = superadmin) and optionally rename", + "operationId": "api_v1_admin_membership_requests_revoke_create", + "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", "parameters": [ { "in": "path", @@ -45370,27 +45907,26 @@ } ], "tags": [ - "QR assets" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrRebindTarget" + "$ref": "#/components/schemas/Revoke" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/QrRebindTarget" + "$ref": "#/components/schemas/Revoke" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/QrRebindTarget" + "$ref": "#/components/schemas/Revoke" } } - }, - "required": true + } }, "security": [ { @@ -45402,22 +45938,90 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrRebindResult" + "$ref": "#/components/schemas/MembershipRequest" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, "409": { - "description": "Outstanding loan or target QR conflict." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/admin/qr/{id}/revoke": { + "/api/v1/admin/membership-terms/{id}/cancel": { "post": { - "operationId": "api_v1_admin_qr_revoke_create", - "summary": "Revoke active QR code", + "operationId": "api_v1_admin_membership_terms_cancel_create", + "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", + "summary": "Cancel an active term", "parameters": [ { "in": "path", @@ -45429,7 +46033,7 @@ } ], "tags": [ - "QR assets" + "Admin memberships" ], "security": [ { @@ -45441,264 +46045,202 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrCode" + "$ref": "#/components/schemas/MembershipTerm" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/qr/boxes": { - "post": { - "operationId": "api_v1_admin_qr_boxes_create", - "summary": "Create a QR-coded box", - "tags": [ - "QR assets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateBoxQr" - }, - "examples": { - "CreateAQR-codedBox": { - "value": { - "makerspace_id": 1, - "label": "Electronics Box A", - "location": "Bench Storage", - "description": "Issued hardware kit box" - }, - "summary": "Create a QR-coded box" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CreateBoxQr" + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CreateBoxQr" + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/qr/containers": { - "post": { - "operationId": "api_v1_admin_qr_containers_create", - "summary": "Create a QR-coded box", - "tags": [ - "QR assets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateBoxQr" - }, - "examples": { - "CreateAQR-codedBox": { - "value": { - "makerspace_id": 1, - "label": "Electronics Box A", - "location": "Bench Storage", - "description": "Issued hardware kit box" - }, - "summary": "Create a QR-coded box" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CreateBoxQr" - } + "description": "" + } + } + } + }, + "/api/v1/admin/memberships": { + "get": { + "operationId": "api_v1_admin_memberships_retrieve", + "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", + "parameters": [ + { + "in": "query", + "name": "id", + "schema": { + "type": "integer" }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CreateBoxQr" - } - } + "description": "Select one membership within the authorized roster." }, - "required": true - }, + { + "in": "query", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "ordering", + "schema": { + "type": "string" + }, + "description": "Allowlisted sort field; prefix with '-' for descending." + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number, 24 rows per page." + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Search within the authorized list." + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string" + } + } + ], + "tags": [ + "Admin memberships" + ], "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Box" + "$ref": "#/components/schemas/MembershipListResponse" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/qr/resolve": { - "post": { - "operationId": "api_v1_admin_qr_resolve_create", - "description": "Resolve the opaque payload encoded in a physical QR label to its target (box, product, or asset) and the scanner actions the caller may take. `payload` is the raw 32-char lowercase hex token printed on the label (Python `uuid4().hex`, e.g. `3f9a1c2b4d5e6f7081920a1b2c3d4e5f`) - it is the value stored as `QrCode.payload` (and `Box.code` for boxes), not a URL or JSON. Resolving a QR also records an immutable scanner-lookup scan event.", - "summary": "Resolve QR target and scanner allowed actions", - "tags": [ - "QR assets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QrResolve" - }, - "examples": { - "ResolveAScannedQRPayload": { - "value": { - "payload": "3f9a1c2b4d5e6f7081920a1b2c3d4e5f" - }, - "summary": "Resolve a scanned QR payload" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/QrResolve" + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/QrResolve" - } - } + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrResolveResult" - }, - "examples": { - "ResolvedBoxQR": { - "value": { - "qr": { - "id": 12, - "makerspace": 1, - "payload": "3f9a1c2b4d5e6f7081920a1b2c3d4e5f", - "target_type": "box", - "target_id": 5, - "status": "active", - "created_at": "2026-06-27T09:30:00Z", - "updated_at": "2026-06-27T09:30:00Z", - "revoked_at": null - }, - "target": { - "type": "box", - "id": 5, - "label": "Electronics Box A", - "code": "3f9a1c2b4d5e6f7081920a1b2c3d4e5f" - }, - "allowed_actions": [ - "contents", - "move_container", - "record_scan", - "revoke", - "view" - ] - }, - "summary": "Resolved box QR" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - } - } - } - }, - "/api/v1/admin/qr/scan": { - "post": { - "operationId": "api_v1_admin_qr_scan_create", - "description": "Context is limited to issue or return and scan events are immutable.", - "summary": "Record a QR scan", - "tags": [ - "QR assets" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QrScan" - }, - "examples": { - "ScanQRDuringIssue": { - "value": { - "payload": "3f9a1c2b4d5e6f7081920a1b2c3d4e5f", - "context": "issue", - "request_id": 99 - }, - "summary": "Scan QR during issue" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/QrScan" + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/QrScan" + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } - } + }, + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QrScanResult" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -45707,56 +46249,46 @@ } } }, - "/api/v1/admin/qr/tools": { - "post": { - "operationId": "api_v1_admin_qr_tools_create", - "summary": "Create or reuse a QR code for a product or asset", + "/api/v1/admin/memberships/{id}": { + "delete": { + "operationId": "api_v1_admin_memberships_destroy", + "description": "Remove a single makerspace membership (un-assign a delegable role).\n\nScope contract (mirrors the create path's non-escalation model): a Space Manager may\nrevoke ONLY delegable-role memberships within their MANAGE_MAKERSPACE scope; a superadmin\nmay revoke any, except inside a superadmin-hidden makerspace (governance hard-block ->\n404). 404-before-403: out-of-scope existence is hidden as 404, a delegable-scope actor\naiming at a SPACE_MANAGER gets 403.", + "summary": "Revoke a makerspace staff membership", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "QR assets" + "Admin users" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateToolQr" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/CreateToolQr" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/CreateToolQr" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QrCode" - } - } - }, - "description": "" + "204": { + "description": "Membership revoked." + }, + "403": { + "description": "A Space Manager membership can only be revoked by a superadmin." + }, + "404": { + "description": "Membership not found or outside the actor's makerspace scope." } } } }, - "/api/v1/admin/report-schedules/{id}": { + "/api/v1/admin/memberships/{id}/capabilities": { "patch": { - "operationId": "api_v1_admin_report_schedules_partial_update", - "summary": "Update report schedule", + "operationId": "api_v1_admin_memberships_capabilities_partial_update", + "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", "parameters": [ { "in": "path", @@ -45768,23 +46300,23 @@ } ], "tags": [ - "Reports" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedReportSchedule" + "$ref": "#/components/schemas/PatchedMembershipCapabilities" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedReportSchedule" + "$ref": "#/components/schemas/PatchedMembershipCapabilities" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedReportSchedule" + "$ref": "#/components/schemas/PatchedMembershipCapabilities" } } } @@ -45799,7 +46331,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportSchedule" + "$ref": "#/components/schemas/AdminMembership" } } }, @@ -45809,67 +46341,59 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid schedule or module disabled." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Makerspace or schedule not found." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant writes are frozen." + "description": "" }, - "503": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant write gate unavailable." + "description": "" } } - }, - "delete": { - "operationId": "api_v1_admin_report_schedules_destroy", - "summary": "Delete report schedule", + } + }, + "/api/v1/admin/memberships/{id}/revoke": { + "post": { + "operationId": "api_v1_admin_memberships_revoke_create", + "description": "Requires manage_members in this makerspace; staff role administration and waiver publishing require manage_makerspace.", "parameters": [ { "in": "path", @@ -45881,84 +46405,120 @@ } ], "tags": [ - "Reports" + "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Revoke" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Revoke" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Revoke" + } + } + } + }, "security": [ { "jwtAuth": [] } ], "responses": { - "204": { - "description": "No response body" + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminMembership" + } + } + }, + "description": "" }, "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid schedule or module disabled." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or schedule not found." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant writes are frozen." + "description": "" }, "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant write gate unavailable." + "description": "" } } } }, - "/api/v1/admin/report-schedules/{id}/run-now": { - "post": { - "operationId": "api_v1_admin_report_schedules_run_now_create", - "summary": "Run a report schedule now", + "/api/v1/admin/memberships/{id}/role": { + "patch": { + "operationId": "api_v1_admin_memberships_role_partial_update", + "description": "Requires manage_members; staff role changes require manage_makerspace. Replace the complete role set using exactly one of role_id or role_ids. Duplicate IDs are ignored; Space Manager and Member are exclusive. Unknown or foreign IDs return 404.", "parameters": [ { "in": "path", @@ -45970,8 +46530,27 @@ } ], "tags": [ - "Reports" + "Admin memberships" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedRoleSetInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedRoleSetInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedRoleSetInput" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -45982,214 +46561,103 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportDelivery" + "$ref": "#/components/schemas/AdminMembership" } } }, "description": "" }, - "409": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "type": "object", + "additionalProperties": {} } } }, - "description": "Schedule inactive or skipped." + "description": "Field validation errors. Provide exactly one of role_id or role_ids; the set must be nonempty, and Space Manager and Member are exclusive." }, - "400": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid schedule or module disabled." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or schedule not found." + "description": "" }, "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant writes are frozen." + "description": "" }, "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant write gate unavailable." + "description": "" } } } }, - "/api/v1/admin/reports/{report_key}/export": { + "/api/v1/admin/memberships/{id}/terms": { "get": { - "operationId": "api_v1_admin_reports_export_retrieve", - "summary": "Export aggregate report", + "operationId": "api_v1_admin_memberships_terms_list", + "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", + "summary": "List the terms of a membership", "parameters": [ - { - "in": "query", - "name": "end", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "format", - "schema": { - "type": "string", - "enum": [ - "csv", - "xlsx" - ] - } - }, - { - "in": "query", - "name": "grain", - "schema": { - "type": "string", - "enum": [ - "day", - "month", - "week" - ] - } - }, - { - "in": "query", - "name": "months", - "schema": { - "type": "integer" - }, - "description": "Retention anniversary in months (1-120); default 3, 6 and 12." - }, { "in": "path", - "name": "report_key", + "name": "id", "schema": { - "type": "string", - "enum": [ - "active-loans", - "booking-utilization", - "cash-book", - "certification-coverage", - "communications-health", - "community-engagement", - "damaged-lost", - "damaged-missing", - "event-attendance", - "evidence-compliance", - "fablab-health", - "import-quality", - "inventory-control", - "loan-throughput", - "machine-service", - "machine-usage", - "maintenance-activity", - "member-activity", - "member-retention", - "membership-revenue", - "module-operational-health", - "most-lent", - "payment-cash-book", - "payment-reconciliation", - "printer-service", - "procurement-performance", - "qr-scans", - "recently-added", - "returns", - "revenue-over-time", - "summary", - "taken-items", - "top-borrowers" - ] + "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "start", - "schema": { - "type": "string", - "format": "date" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "canceled", - "paid_offline", - "paid_online", - "pending", - "waived" - ] - } - }, - { - "in": "query", - "name": "subject_type", - "schema": { - "type": "string", - "enum": [ - "booking", - "event_registration", - "loan_deposit", - "loan_late_fee", - "machine_service_request", - "makerspace_membership", - "membership_request", - "membership_term" - ] - } } ], "tags": [ - "Reports" + "Admin memberships" ], "security": [ { @@ -46199,15 +46667,12 @@ "responses": { "200": { "content": { - "text/csv": { - "schema": { - "type": "string" - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "type": "array", + "items": { + "$ref": "#/components/schemas/MembershipTerm" + } } } }, @@ -46217,115 +46682,58 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid report request." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Makerspace or report not found." - } - } - } - }, - "/api/v1/admin/reports/catalog": { - "get": { - "operationId": "api_v1_admin_reports_catalog_retrieve", - "summary": "List deployment report catalog", - "tags": [ - "Reports" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportCatalog" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Invalid report request." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReportError" - } - } - }, - "description": "Permission denied." - }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReportError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace or report not found." + "description": "" } } - } - }, - "/api/v1/admin/requests/{id}/accept": { + }, "post": { - "operationId": "api_v1_admin_requests_accept_create", - "summary": "Accept borrow request", + "operationId": "api_v1_admin_memberships_terms_create", + "description": "Requires manage_members in this makerspace. Staff role administration and membership plan definitions require manage_makerspace.", + "summary": "Open a term for a membership", "parameters": [ { "in": "path", @@ -46337,26 +46745,27 @@ } ], "tags": [ - "Admin requests" + "Admin memberships" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AcceptRequest" + "$ref": "#/components/schemas/MembershipTermCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/AcceptRequest" + "$ref": "#/components/schemas/MembershipTermCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/AcceptRequest" + "$ref": "#/components/schemas/MembershipTermCreate" } } - } + }, + "required": true }, "security": [ { @@ -46364,11 +46773,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminRequest" + "$ref": "#/components/schemas/MembershipTerm" } } }, @@ -46382,7 +46791,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -46392,7 +46801,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -46402,7 +46811,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -46412,7 +46821,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -46422,15 +46831,15 @@ } } }, - "description": "Workflow conflict." + "description": "" } } } }, - "/api/v1/admin/requests/{id}/assign-box": { + "/api/v1/admin/memberships/{id}/unverify": { "post": { - "operationId": "api_v1_admin_requests_assign_box_create", - "summary": "Assign box to accepted request", + "operationId": "api_v1_admin_memberships_unverify_create", + "description": "Requires manage_members or an active membership with can_verify in this makerspace. Delegated verifiers cannot unverify themselves.", "parameters": [ { "in": "path", @@ -46442,28 +46851,8 @@ } ], "tags": [ - "Admin requests" + "Admin memberships" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AssignBox" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/AssignBox" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/AssignBox" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -46474,7 +46863,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminRequest" + "$ref": "#/components/schemas/AdminMembership" } } }, @@ -46488,7 +46877,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -46498,7 +46887,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -46508,7 +46897,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -46518,7 +46907,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -46528,15 +46917,15 @@ } } }, - "description": "Workflow conflict." + "description": "" } } } }, - "/api/v1/admin/requests/{id}/issue": { + "/api/v1/admin/memberships/{id}/verify": { "post": { - "operationId": "api_v1_admin_requests_issue_create", - "summary": "Issue accepted request", + "operationId": "api_v1_admin_memberships_verify_create", + "description": "Requires manage_members or an active membership with can_verify in this makerspace. Delegated verifiers cannot verify themselves.", "parameters": [ { "in": "path", @@ -46548,28 +46937,8 @@ } ], "tags": [ - "Admin requests" + "Admin memberships" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/IssueRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/IssueRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/IssueRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -46580,7 +46949,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminRequest" + "$ref": "#/components/schemas/AdminMembership" } } }, @@ -46594,7 +46963,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -46604,7 +46973,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -46614,7 +46983,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -46624,7 +46993,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -46634,25 +47003,14 @@ } } }, - "description": "Workflow conflict." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Service unavailable." + "description": "" } } } }, - "/api/v1/admin/requests/{id}/reject": { + "/api/v1/admin/memberships/{id}/waiver/witness": { "post": { - "operationId": "api_v1_admin_requests_reject_create", - "summary": "Reject borrow request", + "operationId": "api_v1_admin_memberships_waiver_witness_create", "parameters": [ { "in": "path", @@ -46664,28 +47022,8 @@ } ], "tags": [ - "Admin requests" + "Admin memberships" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RejectRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/RejectRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RejectRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -46696,7 +47034,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminRequest" + "$ref": "#/components/schemas/WitnessWaiverResponse" } } }, @@ -46710,7 +47048,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -46720,7 +47058,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -46730,7 +47068,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -46740,7 +47078,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -46750,15 +47088,15 @@ } } }, - "description": "Workflow conflict." + "description": "" } } } }, - "/api/v1/admin/requests/{id}/return": { - "post": { - "operationId": "api_v1_admin_requests_return_create", - "summary": "Return issued request items", + "/api/v1/admin/organization-invitations/{id}/": { + "delete": { + "operationId": "api_v1_admin_organization_invitations_destroy", + "summary": "Revoke an unused organization invitation", "parameters": [ { "in": "path", @@ -46770,53 +47108,16 @@ } ], "tags": [ - "Admin requests" + "Admin organizations" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReturnRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ReturnRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ReturnRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AdminRequest" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid request." + "204": { + "description": "No response body" }, "401": { "content": { @@ -46826,7 +47127,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -46836,7 +47137,7 @@ } } }, - "description": "Permission denied." + "description": "Organization authority is required." }, "404": { "content": { @@ -46846,19 +47147,12 @@ } } }, - "description": "Not found." + "description": "Organization not found." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Workflow conflict." + "400": { + "description": "Invalid organization data." }, - "503": { + "409": { "content": { "application/json": { "schema": { @@ -46866,48 +47160,36 @@ } } }, - "description": "Service unavailable." + "description": "Organization state conflict." } } } }, - "/api/v1/admin/requests/{id}/return-due": { - "post": { - "operationId": "api_v1_admin_requests_return_due_create", - "summary": "Set request return due time", + "/api/v1/admin/organizations/": { + "get": { + "operationId": "admin_organizations_list", + "summary": "List organizations visible to the actor", "parameters": [ { - "in": "path", - "name": "id", + "in": "query", + "name": "active", "schema": { - "type": "integer" + "type": "boolean" }, - "required": true + "description": "Filter by active status: true or false." + }, + { + "in": "query", + "name": "search", + "schema": { + "type": "string" + }, + "description": "Case-insensitive substring of organization name or slug." } ], "tags": [ - "Admin requests" + "Admin organizations" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReturnDue" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ReturnDue" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ReturnDue" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -46918,22 +47200,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminRequest" + "$ref": "#/components/schemas/OrganizationList" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid request." - }, "401": { "content": { "application/json": { @@ -46942,7 +47214,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -46952,7 +47224,7 @@ } } }, - "description": "Permission denied." + "description": "Organization authority is required." }, "404": { "content": { @@ -46962,58 +47234,50 @@ } } }, - "description": "Not found." + "description": "Organization not found." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Workflow conflict." + "400": { + "description": "Invalid active filter; use true or false." } } - } - }, - "/api/v1/admin/requests/{id}/timeline": { - "get": { - "operationId": "api_v1_admin_requests_timeline_retrieve", - "description": "Scans, evidence, asset outcomes, accountability, problem reports and related audit rows (`kind=audit`; metadata is the presentation-catalogue projection), ordered by (at, kind, source id). Events with an `evidence_id` carry `evidence_retention` (`null` = no retention-state row, not proof the bytes exist). No object key or signed URL is ever returned; photo bytes stay behind the UPLOAD_EVIDENCE-gated evidence endpoint.", - "summary": "Read-only immutable timeline for one hardware request", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - }, - "description": "Maximum history events to return. Defaults to 200; capped at 500." - } - ], + }, + "post": { + "operationId": "api_v1_admin_organizations_create", + "summary": "Create an organization (superadmin only)", "tags": [ - "Admin requests" + "Admin organizations" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationCreate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/OrganizationCreate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/OrganizationCreate" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RequestTimelineResponse" + "$ref": "#/components/schemas/OrganizationDetail" } } }, @@ -47023,95 +47287,237 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Invalid request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Not found." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Workflow conflict." + "description": "Conflicts with the current state." } } } }, - "/api/v1/admin/search": { + "/api/v1/admin/organizations/{organization_id}/analytics/{report_key}": { "get": { - "operationId": "api_v1_admin_search_retrieve", - "description": "At most limit hits per entity (default 5, maximum 10). Members match username; bookings match space name or booking ID. Disabled or unauthorized entity lists contribute no hits.", - "summary": "Search authorized staff lists", + "operationId": "api_v1_admin_organizations_analytics_retrieve", + "description": "Requires an active organization membership carrying the report's action. Authorization and the owned-makerspace set are resolved server-side; a combined total is always returned together with its per-makerspace breakdown. A chart_spec projected onto the aggregation strategy's total fields accompanies combinable charts; null means a table-only combined projection.", + "summary": "Get organization analytics report", "parameters": [ { "in": "query", - "name": "limit", + "name": "end", "schema": { - "type": "integer", - "maximum": 10, - "minimum": 1, - "default": 5 + "type": "string", + "format": "date" } }, { "in": "query", - "name": "makerspace_id", + "name": "grain", "schema": { - "type": "integer", - "maximum": 9223372036854775807, - "minimum": 1, - "format": "int64" + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "in": "path", + "name": "organization_id", + "schema": { + "type": "integer" }, "required": true }, { - "in": "query", - "name": "q", + "in": "path", + "name": "report_key", "schema": { "type": "string", - "maxLength": 100, - "minLength": 2 + "enum": [ + "asset-usage", + "audit-activity", + "booking-utilization", + "cash-book", + "communications-health", + "consumable-pools", + "consumable-usage", + "damaged-lost", + "damaged-missing", + "device-health", + "event-attendance", + "event-feedback-scores", + "event-funnel", + "event-revenue", + "event-signups", + "evidence-compliance", + "fablab-health", + "inventory-control", + "inventory-utilisation", + "inventory-watch-demand", + "loan-throughput", + "machine-downtime", + "machine-errors", + "machine-service", + "machine-usage", + "maintenance-activity", + "member-activity", + "member-retention", + "membership-growth", + "membership-revenue", + "mobile-auth", + "mobile-devices", + "most-lent", + "organization-membership", + "payment-cash-book", + "payment-reconciliation", + "payments-ageing", + "presence-activity", + "printer-jobs", + "problem-reports", + "procurement-performance", + "qr-scans", + "recently-added", + "request-funnel", + "revenue-over-time", + "stock-health", + "stock-transfers", + "stocktake-variance", + "summary", + "taken-items", + "warranty-expiry" + ] }, "required": true + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] + } } ], "tags": [ - "Staff lists" + "Analytics" ], "security": [ { @@ -47123,7 +47529,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SearchOutput" + "$ref": "#/components/schemas/OrganizationReportResponse" } } }, @@ -47133,71 +47539,61 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid or excluded organization report." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Organization or report not found." } } } }, - "/api/v1/admin/space-pack-profiles": { + "/api/v1/admin/organizations/{id}/": { "get": { - "operationId": "api_v1_admin_space_pack_profiles_list", + "operationId": "admin_organizations_retrieve", + "summary": "Retrieve organization governance details", "parameters": [ { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", + "in": "path", + "name": "id", "schema": { "type": "integer" - } + }, + "required": true } ], "tags": [ - "Platform" + "Admin organizations" ], "security": [ { @@ -47209,17 +47605,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedPackProfileList" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/OrganizationDetail" } } }, @@ -47229,88 +47615,68 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Authentication is required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Organization authority is required." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Organization not found." } } }, - "post": { - "operationId": "api_v1_admin_space_pack_profiles_create", + "patch": { + "operationId": "api_v1_admin_organizations_partial_update", + "summary": "Update an organization public profile", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Platform" + "Admin organizations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackProfile" + "$ref": "#/components/schemas/PatchedOrganizationProfileUpdate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PackProfile" + "$ref": "#/components/schemas/PatchedOrganizationProfileUpdate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PackProfile" + "$ref": "#/components/schemas/PatchedOrganizationProfileUpdate" } } - }, - "required": true + } }, "security": [ { @@ -47318,93 +47684,136 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackProfile" + "$ref": "#/components/schemas/OrganizationDetail" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Authentication is required." }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Organization authority is required." }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Organization not found." }, - "404": { + "400": { + "description": "Invalid organization data." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Organization state conflict." + } + } + }, + "delete": { + "operationId": "api_v1_admin_organizations_destroy", + "summary": "Delete an unlinked organization after recent re-authentication", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin organizations" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Authentication is required." }, - "423": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Organization authority is required." }, - "503": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PackError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Organization not found." + }, + "400": { + "description": "Invalid organization data." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Organization state conflict." } } } }, - "/api/v1/admin/spaces/{id}/": { + "/api/v1/admin/organizations/{id}/invitations/": { "get": { - "operationId": "api_v1_admin_spaces_retrieve", - "summary": "Retrieve a bookable space", + "operationId": "api_v1_admin_organizations_invitations_retrieve", + "summary": "List organization invitations without bearer tokens", "parameters": [ { "in": "path", @@ -47416,7 +47825,7 @@ } ], "tags": [ - "Admin bookings" + "Admin organizations" ], "security": [ { @@ -47428,22 +47837,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceAdmin" + "$ref": "#/components/schemas/OrganizationInvitationList" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid request." - }, "401": { "content": { "application/json": { @@ -47452,7 +47851,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -47462,7 +47861,7 @@ } } }, - "description": "Permission denied." + "description": "Organization authority is required." }, "404": { "content": { @@ -47472,13 +47871,13 @@ } } }, - "description": "Not found." + "description": "Organization not found." } } }, - "patch": { - "operationId": "api_v1_admin_spaces_partial_update", - "summary": "Update a bookable space", + "post": { + "operationId": "api_v1_admin_organizations_invitations_create", + "summary": "Create a single-use organization invitation", "parameters": [ { "in": "path", @@ -47490,23 +47889,23 @@ } ], "tags": [ - "Admin bookings" + "Admin organizations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedBookableSpaceWrite" + "$ref": "#/components/schemas/OrganizationInvitationCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedBookableSpaceWrite" + "$ref": "#/components/schemas/OrganizationInvitationCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedBookableSpaceWrite" + "$ref": "#/components/schemas/OrganizationInvitationCreate" } } } @@ -47517,17 +47916,17 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceAdmin" + "$ref": "#/components/schemas/OrganizationInvitationCreated" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -47535,9 +47934,9 @@ } } }, - "description": "Invalid request." + "description": "Authentication is required." }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -47545,17 +47944,7 @@ } } }, - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "Organization authority is required." }, "404": { "content": { @@ -47565,7 +47954,10 @@ } } }, - "description": "Not found." + "description": "Organization not found." + }, + "400": { + "description": "Invalid organization data." }, "409": { "content": { @@ -47575,15 +47967,14 @@ } } }, - "description": "Workflow conflict." + "description": "Organization state conflict." } } } }, - "/api/v1/admin/spaces/{id}/booking-rules/": { + "/api/v1/admin/organizations/{id}/makerspaces/": { "get": { - "operationId": "api_v1_admin_spaces_booking_rules_retrieve", - "summary": "Retrieve booking rules for a space", + "operationId": "api_v1_admin_organizations_makerspaces_retrieve", "parameters": [ { "in": "path", @@ -47595,7 +47986,7 @@ } ], "tags": [ - "Admin bookings" + "Admin organizations" ], "security": [ { @@ -47607,48 +47998,82 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceBookingRules" + "$ref": "#/components/schemas/OrganizationLinkPage" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { "type": "object", - "additionalProperties": {} + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Invalid booking-rule values or bookings module disabled." + "description": "Not signed in." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } }, - "patch": { - "operationId": "api_v1_admin_spaces_booking_rules_partial_update", - "summary": "Update booking rules for a space", + "post": { + "operationId": "api_v1_admin_organizations_makerspaces_create", "parameters": [ { "in": "path", @@ -47660,26 +48085,27 @@ } ], "tags": [ - "Admin bookings" + "Admin organizations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedBookableSpaceBookingRules" + "$ref": "#/components/schemas/OrganizationLinkWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedBookableSpaceBookingRules" + "$ref": "#/components/schemas/OrganizationLinkWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedBookableSpaceBookingRules" + "$ref": "#/components/schemas/OrganizationLinkWrite" } } - } + }, + "required": true }, "security": [ { @@ -47691,7 +48117,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceBookingRules" + "$ref": "#/components/schemas/OrganizationLink" } } }, @@ -47702,182 +48128,161 @@ "application/json": { "schema": { "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Invalid booking-rule values or bookings module disabled." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Not found." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Workflow conflict." - } - } - } - }, - "/api/v1/admin/spaces/{id}/bookings/": { - "get": { - "operationId": "api_v1_admin_spaces_bookings_retrieve", - "summary": "List bookings for a space", - "parameters": [ - { - "in": "query", - "name": "booking_id", - "schema": { - "type": "integer", - "maximum": 9223372036854775807, - "minimum": 1, - "format": "int64" - } - }, - { - "in": "query", - "name": "ends_at", - "schema": { - "type": "string", - "format": "date-time" - } - }, - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "ordering", - "schema": { - "type": "string" - }, - "description": "Allowlisted sort field; prefix with '-' for descending." - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - }, - "description": "Page number, 24 rows per page." - }, - { - "in": "query", - "name": "starts_at", - "schema": { - "type": "string", - "format": "date-time" - } + "description": "Not signed in." }, - { - "in": "query", - "name": "status", - "schema": { - "enum": [ - "pending", - "confirmed", - "rejected", - "cancelled", - "completed", - "no_show" - ], - "type": "string", - "minLength": 1 - }, - "description": "* `pending` - Pending\n* `confirmed` - Confirmed\n* `rejected` - Rejected\n* `cancelled` - Cancelled\n* `completed` - Completed\n* `no_show` - No-show" - } - ], - "tags": [ - "Admin bookings" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookingListResponse" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "400": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Invalid request." + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, - "401": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Authentication required." + "description": "Conflicts with the current state." }, - "403": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Permission denied." + "description": "Makerspace writes are frozen for tenant migration." }, - "404": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Not found." + "description": "A required dependency is unavailable." } } - }, - "post": { - "operationId": "api_v1_admin_spaces_bookings_create", - "summary": "Book a space on behalf of an active member", + } + }, + "/api/v1/admin/organizations/{id}/makerspaces/{makerspace_id}/": { + "delete": { + "operationId": "api_v1_admin_organizations_makerspaces_destroy", "parameters": [ { "in": "path", @@ -47886,125 +48291,189 @@ "type": "integer" }, "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true } ], "tags": [ - "Admin bookings" + "Admin organizations" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/StaffBookingInput" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/StaffBookingInput" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/StaffBookingInput" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BookingAdmin" - } - } - }, - "description": "" + "204": { + "description": "No response body" }, "400": { "content": { "application/json": { "schema": { "type": "object", - "additionalProperties": {} + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Invalid input or bookings module disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Workflow conflict." + "description": "Conflicts with the current state." }, "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Tenant frozen for migration." + "description": "Makerspace writes are frozen for tenant migration." }, "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Tenant write gate unavailable." + "description": "A required dependency is unavailable." } } } }, - "/api/v1/admin/spaces/{id}/deactivate/": { - "post": { - "operationId": "api_v1_admin_spaces_deactivate_create", - "summary": "Deactivate a bookable space", + "/api/v1/admin/organizations/{id}/memberships/": { + "get": { + "operationId": "api_v1_admin_organizations_memberships_retrieve", + "summary": "List organization memberships", "parameters": [ { "in": "path", @@ -48016,7 +48485,7 @@ } ], "tags": [ - "Admin bookings" + "Admin organizations" ], "security": [ { @@ -48028,22 +48497,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceAdmin" + "$ref": "#/components/schemas/OrganizationMembershipList" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid request." - }, "401": { "content": { "application/json": { @@ -48052,7 +48511,7 @@ } } }, - "description": "Authentication required." + "description": "Authentication is required." }, "403": { "content": { @@ -48062,7 +48521,7 @@ } } }, - "description": "Permission denied." + "description": "Organization authority is required." }, "404": { "content": { @@ -48072,25 +48531,14 @@ } } }, - "description": "Not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Workflow conflict." + "description": "Organization not found." } } } }, - "/api/v1/admin/spaces/{id}/eligible-members/": { - "get": { - "operationId": "api_v1_admin_spaces_eligible_members_retrieve", - "summary": "List active members for staff booking", + "/api/v1/admin/organizations/{id}/memberships/{membership_id}/": { + "patch": { + "operationId": "api_v1_admin_organizations_memberships_partial_update", "parameters": [ { "in": "path", @@ -48101,16 +48549,36 @@ "required": true }, { - "in": "query", - "name": "page", + "in": "path", + "name": "membership_id", "schema": { "type": "integer" - } + }, + "required": true } ], "tags": [ - "Admin bookings" + "Admin organizations" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedOrganizationMembershipUpdate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedOrganizationMembershipUpdate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedOrganizationMembershipUpdate" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -48121,7 +48589,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookingEligibleMemberList" + "$ref": "#/components/schemas/OrganizationMembership" } } }, @@ -48132,61 +48600,121 @@ "application/json": { "schema": { "type": "object", - "additionalProperties": {} + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Invalid input or bookings module disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } + } + } + }, + "description": "Conflicts with the current state." } } } }, - "/api/v1/admin/spaces/{id}/image/": { - "delete": { - "operationId": "api_v1_admin_spaces_image_destroy", - "summary": "Delete a space image", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/admin/organized-events/": { + "get": { + "operationId": "api_v1_admin_organized_events_retrieve", + "description": "List the events this actor's organizations organize, across venues.\n\nOrganizer authority is deliberately per-event and grants nothing over the venue, so an\norganizer at a venue their organization is not linked to cannot use the venue's event\nlist -- which left the per-event endpoints reachable only by someone who already knew a\ndatabase id. This is the discoverable surface for that authority: it lists exactly the\nevents the organizer predicate matches and confers no venue authority whatsoever.", + "summary": "List events organized by the actor's organizations", "tags": [ - "Admin bookings" + "Events" ], "security": [ { @@ -48194,18 +48722,15 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "400": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/EventListResponse" } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -48215,7 +48740,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -48225,7 +48750,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -48235,58 +48760,68 @@ } } }, - "description": "Not found." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Service unavailable." + "description": "" } } } }, - "/api/v1/admin/spaces/{id}/image/finalize/": { - "post": { - "operationId": "api_v1_admin_spaces_image_finalize_create", - "summary": "Finalize and attach a space image", + "/api/v1/admin/platform/analytics/{report_key}": { + "get": { + "operationId": "api_v1_admin_platform_analytics_retrieve", + "description": "Platform-only counts with the definition's typed chart_spec. Hidden tenants appear solely as anonymous daily platform-service counts; own-provider and business usage exclude them. Connect includes attributed ledger facts, with no provider-attempt failure history.", + "summary": "Get superadmin platform analytics", "parameters": [ { - "in": "path", - "name": "id", + "in": "query", + "name": "end", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "limit", "schema": { "type": "integer" + } + }, + { + "in": "path", + "name": "report_key", + "schema": { + "type": "string", + "enum": [ + "platform-services", + "platform-usage" + ] }, "required": true + }, + { + "in": "query", + "name": "start", + "schema": { + "type": "string", + "format": "date" + } } ], "tags": [ - "Admin bookings" + "Analytics" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SpaceImageFinalizeRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SpaceImageFinalizeRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SpaceImageFinalizeRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -48297,7 +48832,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookableSpaceAdmin" + "$ref": "#/components/schemas/PlatformAnalyticsReport" } } }, @@ -48307,17 +48842,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Invalid request." + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, @@ -48327,7 +48862,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, @@ -48337,73 +48872,51 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Service unavailable." + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/spaces/{id}/image/presign/": { - "post": { - "operationId": "api_v1_admin_spaces_image_presign_create", - "summary": "Create a space image upload URL", + "/api/v1/admin/platform/analytics/makerspaces": { + "get": { + "operationId": "api_v1_admin_platform_analytics_makerspaces_retrieve", + "description": "Paginated picker. A hidden makerspace requires an active local membership granting the selected report's action. Platform analytics always use their stricter visibility policy.", + "summary": "List makerspaces available for tenant analytics", "parameters": [ { - "in": "path", - "name": "id", + "in": "query", + "name": "page", "schema": { "type": "integer" + } + }, + { + "in": "query", + "name": "report_key", + "schema": { + "type": "string" }, - "required": true + "description": "Tenant report key; defaults to summary." } ], "tags": [ - "Admin bookings" + "Analytics" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SpaceImagePresignRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SpaceImagePresignRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SpaceImagePresignRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SpaceImagePresignResponse" + "$ref": "#/components/schemas/AnalyticsMakerspacePage" } } }, @@ -48413,17 +48926,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Invalid request." + "description": "Invalid report request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, @@ -48433,7 +48946,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, @@ -48443,57 +48956,80 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Service unavailable." + "description": "Makerspace or report not found." } } } }, - "/api/v1/admin/spaces/{id}/recurring-bookings/": { - "post": { - "operationId": "api_v1_admin_spaces_recurring_bookings_create", - "description": "Create a finite weekly series in makerspace local time. Each occurrence has its own flat charge and cancellation. Conflicts are reported, never silently skipped.", + "/api/v1/admin/platform/backup-destinations": { + "get": { + "operationId": "api_v1_admin_platform_backup_destinations_retrieve", + "summary": "List offsite backup destinations", "parameters": [ { - "in": "path", - "name": "id", + "in": "query", + "name": "page", "schema": { "type": "integer" }, - "required": true + "description": "Page number (24 rows per page)." } ], "tags": [ - "Admin bookings" + "Backup destinations" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BackupDestinationPage" + } + } + }, + "description": "" + }, + "404": { + "description": "The requested resource does not exist in the actor's scope." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." + } + } + }, + "post": { + "operationId": "api_v1_admin_platform_backup_destinations_create", + "summary": "Register an S3-compatible offsite backup destination", + "tags": [ + "Backup destinations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecurringBookingInput" + "$ref": "#/components/schemas/BackupDestination" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/RecurringBookingInput" + "$ref": "#/components/schemas/BackupDestination" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/RecurringBookingInput" + "$ref": "#/components/schemas/BackupDestination" } } }, @@ -48505,48 +49041,36 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecurrenceResult" + "$ref": "#/components/schemas/BackupDestination" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "description": "The request is invalid for the current lifecycle state." }, "401": { - "description": "Not signed in." + "description": "Authentication is required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "409": { - "description": "Conflicts with the current state." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." - }, - "503": { - "description": "A required dependency is unavailable." + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/stock-transfers/{id}": { + "/api/v1/admin/platform/backup-destinations/{destination_id}": { "get": { - "operationId": "api_v1_admin_stock_transfers_retrieve", - "summary": "Retrieve stock transfer", + "operationId": "api_v1_admin_platform_backup_destinations_retrieve_2", + "summary": "Get one offsite backup destination", "parameters": [ { "in": "path", - "name": "id", + "name": "destination_id", "schema": { "type": "integer" }, @@ -48554,7 +49078,7 @@ } ], "tags": [ - "Stock transfers" + "Backup destinations" ], "security": [ { @@ -48566,42 +49090,30 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StockTransfer" + "$ref": "#/components/schemas/BackupDestination" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GenericObject" - } - } - }, - "description": "Invalid request." + "404": { + "description": "The requested resource does not exist in the actor's scope." }, "401": { - "description": "Authentication credentials were not provided." + "description": "Authentication is required." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "The authenticated actor is not authorized." } } - } - }, - "/api/v1/admin/stocktakes/{id}": { - "get": { - "operationId": "api_v1_admin_stocktakes_retrieve", - "summary": "Retrieve stocktake", + }, + "patch": { + "operationId": "api_v1_admin_platform_backup_destinations_partial_update", + "summary": "Update or rotate credentials of an offsite destination", "parameters": [ { "in": "path", - "name": "id", + "name": "destination_id", "schema": { "type": "integer" }, @@ -48609,8 +49121,27 @@ } ], "tags": [ - "Stocktake" + "Backup destinations" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedBackupDestination" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedBackupDestination" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedBackupDestination" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -48621,42 +49152,35 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Stocktake" + "$ref": "#/components/schemas/BackupDestination" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GenericObject" - } - } - }, - "description": "Invalid request or stocktake state." + "description": "The request is invalid for the current lifecycle state." + }, + "404": { + "description": "The requested resource does not exist in the actor's scope." }, "401": { - "description": "Authentication credentials were not provided." + "description": "Authentication is required." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/stocktakes/{id}/apply-adjustments": { + "/api/v1/admin/platform/backup-destinations/{destination_id}/retire": { "post": { - "operationId": "api_v1_admin_stocktakes_apply_adjustments_create", - "summary": "Apply stocktake adjustments", + "operationId": "api_v1_admin_platform_backup_destinations_retire_create", + "summary": "Retire a destination: stop pushing and wipe its stored credentials", "parameters": [ { "in": "path", - "name": "id", + "name": "destination_id", "schema": { "type": "integer" }, @@ -48664,7 +49188,7 @@ } ], "tags": [ - "Stocktake" + "Backup destinations" ], "security": [ { @@ -48676,42 +49200,35 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Stocktake" + "$ref": "#/components/schemas/BackupDestination" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GenericObject" - } - } - }, - "description": "Invalid request or stocktake state." + "description": "The request is invalid for the current lifecycle state." + }, + "404": { + "description": "The requested resource does not exist in the actor's scope." }, "401": { - "description": "Authentication credentials were not provided." + "description": "Authentication is required." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/stocktakes/{id}/approve": { + "/api/v1/admin/platform/backup-destinations/{destination_id}/test": { "post": { - "operationId": "api_v1_admin_stocktakes_approve_create", - "summary": "Approve stocktake", + "operationId": "api_v1_admin_platform_backup_destinations_test_create", + "summary": "Probe write, read-back and delete on a destination", "parameters": [ { "in": "path", - "name": "id", + "name": "destination_id", "schema": { "type": "integer" }, @@ -48719,7 +49236,7 @@ } ], "tags": [ - "Stocktake" + "Backup destinations" ], "security": [ { @@ -48731,50 +49248,48 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Stocktake" + "$ref": "#/components/schemas/DestinationProbe" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GenericObject" - } - } - }, - "description": "Invalid request or stocktake state." + "404": { + "description": "The requested resource does not exist in the actor's scope." }, "401": { - "description": "Authentication credentials were not provided." + "description": "Authentication is required." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/stocktakes/{id}/complete": { - "post": { - "operationId": "api_v1_admin_stocktakes_complete_create", - "summary": "Complete stocktake", + "/api/v1/admin/platform/backup-destinations/{destination_id}/uploads": { + "get": { + "operationId": "api_v1_admin_platform_backup_destinations_uploads_retrieve", + "summary": "List the upload journal of one destination", "parameters": [ { "in": "path", - "name": "id", + "name": "destination_id", "schema": { "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number (24 rows per page)." } ], "tags": [ - "Stocktake" + "Backup destinations" ], "security": [ { @@ -48786,145 +49301,114 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Stocktake" + "$ref": "#/components/schemas/BackupDestinationUploadPage" } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GenericObject" - } - } - }, - "description": "Invalid request or stocktake state." + "404": { + "description": "The requested resource does not exist in the actor's scope." }, "401": { - "description": "Authentication credentials were not provided." + "description": "Authentication is required." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/stocktakes/{id}/count-lines": { + "/api/v1/admin/platform/backup-destinations/push": { "post": { - "operationId": "api_v1_admin_stocktakes_count_lines_create", - "summary": "Count stocktake line", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_admin_platform_backup_destinations_push_create", + "summary": "Start an offsite push now instead of waiting for the schedule", "tags": [ - "Stocktake" + "Backup destinations" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/StocktakeLineInput" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/StocktakeLineInput" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/StocktakeLineInput" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StocktakeLine" + "$ref": "#/components/schemas/DestinationDispatch" } } }, "description": "" }, - "400": { + "503": { + "description": "The backup worker is unavailable." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." + } + } + } + }, + "/api/v1/admin/platform/backup-settings": { + "get": { + "operationId": "api_v1_admin_platform_backup_settings_retrieve", + "summary": "Get deployment backup settings", + "tags": [ + "Backup" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GenericObject" + "$ref": "#/components/schemas/PlatformBackupSettings" } } }, - "description": "Invalid request or stocktake state." + "description": "" }, "401": { - "description": "Authentication credentials were not provided." + "description": "Authentication is required." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "The authenticated actor is not authorized." } } - } - }, - "/api/v1/admin/stocktakes/{id}/resolve-scan": { - "post": { - "operationId": "api_v1_admin_stocktakes_resolve_scan_create", - "summary": "Resolve a scanned QR to a stocktake count target", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + }, + "patch": { + "operationId": "api_v1_admin_platform_backup_settings_partial_update", + "summary": "Update deployment backup settings", "tags": [ - "Stocktake" + "Backup" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StocktakeScanInput" + "$ref": "#/components/schemas/PatchedPlatformBackupSettings" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/StocktakeScanInput" + "$ref": "#/components/schemas/PatchedPlatformBackupSettings" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/StocktakeScanInput" + "$ref": "#/components/schemas/PatchedPlatformBackupSettings" } } - }, - "required": true + } }, "security": [ { @@ -48936,70 +49420,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StocktakeScanResult" + "$ref": "#/components/schemas/PlatformBackupSettings" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GenericObject" - } - } - }, - "description": "Invalid request or stocktake state." + "description": "The request is invalid for the current lifecycle state." }, "401": { - "description": "Authentication credentials were not provided." + "description": "Authentication is required." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/users/{id}/reset-password": { - "post": { - "operationId": "api_v1_admin_users_reset_password_create", - "summary": "Reset a staff user's password (temp + force change)", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/admin/platform/backups": { + "get": { + "operationId": "api_v1_admin_platform_backups_list", + "summary": "List full-deployment backup archives", "tags": [ - "Admin users" + "Backup" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResetPasswordRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ResetPasswordRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ResetPasswordRequest" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -49010,31 +49455,28 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResetPasswordResponse" + "type": "array", + "items": { + "$ref": "#/components/schemas/BackupArchive" + } } } }, "description": "" + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." } } - } - }, - "/api/v1/admin/users/{id}/restore-access": { + }, "post": { - "operationId": "api_v1_admin_users_restore_access_create", - "summary": "Restore user access", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_admin_platform_backups_create", + "summary": "Request an age-encrypted full-deployment backup", "tags": [ - "Admin users" + "Backup" ], "security": [ { @@ -49042,60 +49484,61 @@ } ], "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/User" + "$ref": "#/components/schemas/BackupArchive" } } }, "description": "" + }, + "503": { + "description": "The backup worker is unavailable." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/users/{id}/restrict": { + "/api/v1/admin/platform/backups/{archive_id}/rehydrate": { "post": { - "operationId": "api_v1_admin_users_restrict_create", - "summary": "Restrict or suspend a user", + "operationId": "api_v1_admin_platform_backups_rehydrate_create", + "summary": "Restore an archive's primary bytes from a verified offsite copy", "parameters": [ { "in": "path", - "name": "id", + "name": "archive_id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin users" + "Backup destinations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RestrictUser" - }, - "examples": { - "RestrictARequester": { - "value": { - "status": "restricted", - "reason": "Unreturned loan under review" - }, - "summary": "Restrict a requester" - } + "$ref": "#/components/schemas/Rehydrate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/RestrictUser" + "$ref": "#/components/schemas/Rehydrate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/RestrictUser" + "$ref": "#/components/schemas/Rehydrate" } } }, @@ -49107,36 +49550,75 @@ } ], "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/User" + "$ref": "#/components/schemas/DestinationDispatch" } } }, "description": "" + }, + "400": { + "description": "The request is invalid for the current lifecycle state." + }, + "404": { + "description": "The requested resource does not exist in the actor's scope." + }, + "503": { + "description": "The backup worker is unavailable." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/users/inventory-managers": { + "/api/v1/admin/platform/dashboard": { "get": { - "operationId": "api_v1_admin_users_inventory_managers_list", - "summary": "List or create staff memberships", - "parameters": [ + "operationId": "api_v1_admin_platform_dashboard_retrieve", + "summary": "Read platform operations dashboard", + "tags": [ + "Platform" + ], + "security": [ { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } + "jwtAuth": [] } ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlatformDashboard" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + } + } + } + }, + "/api/v1/admin/platform/email-settings": { + "get": { + "operationId": "api_v1_admin_platform_email_settings_retrieve", + "summary": "Retrieve or update platform email settings", "tags": [ - "Admin users" + "Platform" ], "security": [ { @@ -49148,39 +49630,44 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedStaffMembershipList" + "$ref": "#/components/schemas/PlatformEmailSettings" } } }, "description": "" + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." } } }, - "post": { - "operationId": "api_v1_admin_users_inventory_managers_create", - "summary": "List or create staff memberships", + "patch": { + "operationId": "api_v1_admin_platform_email_settings_partial_update", + "summary": "Retrieve or update platform email settings", "tags": [ - "Admin users" + "Platform" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/PatchedPlatformEmailSettings" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/PatchedPlatformEmailSettings" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/PatchedPlatformEmailSettings" } } - }, - "required": true + } }, "security": [ { @@ -49188,36 +49675,67 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/PlatformEmailSettings" } } }, "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." } } } }, - "/api/v1/admin/users/machine-managers": { + "/api/v1/admin/platform/mobile-readiness": { "get": { - "operationId": "api_v1_admin_users_machine_managers_list", - "summary": "List or create staff memberships", - "parameters": [ + "operationId": "api_v1_admin_platform_mobile_readiness_retrieve", + "description": "Read-only configuration checks, grouped by registration/environment and platform. No provider probes or secret values.", + "summary": "Native mobile integration readiness", + "tags": [ + "Platform" + ], + "security": [ { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } + "jwtAuth": [] } ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MobileReadiness" + } + } + }, + "description": "" + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + } + } + } + }, + "/api/v1/admin/platform/payment-settings": { + "get": { + "operationId": "api_v1_admin_platform_payment_settings_retrieve", + "summary": "Retrieve or update platform Stripe Connect settings", "tags": [ - "Admin users" + "Platform" ], "security": [ { @@ -49229,39 +49747,47 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedStaffMembershipList" + "$ref": "#/components/schemas/PlatformStripeConnectSettings" } } }, "description": "" + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Self-hosted deployment: Stripe Connect is a managed-only surface." } } }, - "post": { - "operationId": "api_v1_admin_users_machine_managers_create", - "summary": "List or create staff memberships", + "patch": { + "operationId": "api_v1_admin_platform_payment_settings_partial_update", + "summary": "Retrieve or update platform Stripe Connect settings", "tags": [ - "Admin users" + "Platform" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/PatchedPlatformStripeConnectSettings" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/PatchedPlatformStripeConnectSettings" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/PatchedPlatformStripeConnectSettings" } } - }, - "required": true + } }, "security": [ { @@ -49269,36 +49795,72 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/PlatformStripeConnectSettings" } } }, "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Self-hosted deployment: Stripe Connect is a managed-only surface." } } } }, - "/api/v1/admin/users/space-managers": { + "/api/v1/admin/platform/problems": { "get": { - "operationId": "api_v1_admin_users_space_managers_list", - "summary": "List or create staff memberships", - "parameters": [ - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } + "operationId": "api_v1_admin_platform_problems_retrieve", + "summary": "Run read-only platform problem checks on demand", + "tags": [ + "Platform" + ], + "security": [ + { + "jwtAuth": [] } ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PlatformProblems" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + } + } + } + }, + "/api/v1/admin/platform/restores": { + "get": { + "operationId": "api_v1_admin_platform_restores_list", + "summary": "List deployment restore operations", "tags": [ - "Admin users" + "Backup" ], "security": [ { @@ -49310,35 +49872,44 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedStaffMembershipList" + "type": "array", + "items": { + "$ref": "#/components/schemas/RestoreOperation" + } } } }, "description": "" + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." } } }, "post": { - "operationId": "api_v1_admin_users_space_managers_create", - "summary": "List or create staff memberships", + "operationId": "api_v1_admin_platform_restores_create", + "summary": "Record restore intent for the privileged host supervisor", "tags": [ - "Admin users" + "Backup" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/RestoreCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/RestoreCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/RestoreCreate" } } }, @@ -49350,35 +49921,45 @@ } ], "responses": { - "201": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffMembership" + "$ref": "#/components/schemas/RestoreOperation" } } }, "description": "" + }, + "400": { + "description": "The request is invalid for the current lifecycle state." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." } } } }, - "/api/v1/admin/warranty/{id}/documents": { + "/api/v1/admin/platform/restores/{restore_id}": { "get": { - "operationId": "api_v1_admin_warranty_documents_list", - "summary": "List documents attached to a warranty", + "operationId": "api_v1_admin_platform_restores_retrieve", + "summary": "Get restore stage, diff, and decision deadline", "parameters": [ { "in": "path", - "name": "id", + "name": "restore_id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin warranty" + "Backup" ], "security": [ { @@ -49390,78 +49971,57 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WarrantyDocument" - } + "$ref": "#/components/schemas/RestoreOperation" } } }, "description": "" }, + "404": { + "description": "The requested resource does not exist in the actor's scope." + }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication is required." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "The authenticated actor is not authorized." } } - }, + } + }, + "/api/v1/admin/platform/restores/{restore_id}/decision": { "post": { - "operationId": "api_v1_admin_warranty_documents_create", - "summary": "Finalize an uploaded warranty document", + "operationId": "api_v1_admin_platform_restores_decision_create", + "summary": "Decide a quiesced in-place restore", "parameters": [ { "in": "path", - "name": "id", + "name": "restore_id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Admin warranty" + "Backup" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WarrantyDocumentFinalize" + "$ref": "#/components/schemas/RestoreDecision" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/WarrantyDocumentFinalize" + "$ref": "#/components/schemas/RestoreDecision" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/WarrantyDocumentFinalize" + "$ref": "#/components/schemas/RestoreDecision" } } }, @@ -49473,91 +50033,86 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WarrantyDocument" + "$ref": "#/components/schemas/RestoreOperation" } } }, "description": "" }, "400": { - "description": "Invalid or duplicate warranty document." + "description": "The request is invalid for the current lifecycle state." + }, + "404": { + "description": "The requested resource does not exist in the actor's scope." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication is required." }, "403": { + "description": "The authenticated actor is not authorized." + } + } + } + }, + "/api/v1/admin/platform/social-auth-settings": { + "get": { + "operationId": "api_v1_admin_platform_social_auth_settings_retrieve", + "summary": "Retrieve or update platform social auth settings", + "tags": [ + "Platform" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PlatformSocialAuthSettings" } } }, "description": "" }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "401": { + "description": "Not signed in." }, - "503": { - "description": "Warranty document storage is unavailable." + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." } } - } - }, - "/api/v1/admin/warranty/{id}/documents/presign": { - "post": { - "operationId": "api_v1_admin_warranty_documents_presign_create", - "summary": "Create a warranty document upload URL", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + }, + "patch": { + "operationId": "api_v1_admin_platform_social_auth_settings_partial_update", + "summary": "Retrieve or update platform social auth settings", "tags": [ - "Admin warranty" + "Platform" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WarrantyDocumentPresign" + "$ref": "#/components/schemas/PatchedPlatformSocialAuthSettings" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/WarrantyDocumentPresign" + "$ref": "#/components/schemas/PatchedPlatformSocialAuthSettings" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/WarrantyDocumentPresign" + "$ref": "#/components/schemas/PatchedPlatformSocialAuthSettings" } } - }, - "required": true + } }, "security": [ { @@ -49565,130 +50120,110 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WarrantyDocumentUploadResponse" + "$ref": "#/components/schemas/PlatformSocialAuthSettings" } } }, "description": "" }, "400": { - "description": "Invalid document upload request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + } + } + } + }, + "/api/v1/admin/platform/tenant-migrations/deployment-identity": { + "get": { + "operationId": "api_v1_admin_platform_tenant_migrations_deployment_identity_retrieve", + "summary": "Read this deployment's signing identity and target age recipient", + "tags": [ + "Tenant migration" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeploymentIdentity" } } }, "description": "" }, - "403": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "State conflict." }, - "404": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" - }, - "503": { - "description": "Warranty document storage is unavailable." - } - } - } - }, - "/api/v1/admin/warranty/documents/{id}": { - "delete": { - "operationId": "api_v1_admin_warranty_documents_destroy", - "summary": "Delete a warranty document", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Admin warranty" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" + "description": "Authentication required." }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "403": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "404": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } } }, - "/api/v1/admin/warranty/documents/{id}/url": { + "/api/v1/admin/platform/tenant-migrations/imports": { "get": { - "operationId": "api_v1_admin_warranty_documents_url_retrieve", - "summary": "Create a signed warranty document view URL", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_admin_platform_tenant_migrations_imports_list", + "summary": "List tenant import jobs", "tags": [ - "Admin warranty" + "Tenant migration" ], "security": [ { @@ -49700,7 +50235,10 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WarrantyDocumentUrl" + "type": "array", + "items": { + "$ref": "#/components/schemas/ImportJob" + } } } }, @@ -49710,60 +50248,65 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "503": { - "description": "Warranty document storage is unavailable." + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } - } - }, - "/api/v1/auth/change-password": { + }, "post": { - "operationId": "api_v1_auth_change_password_create", - "summary": "Change current user's password", + "operationId": "api_v1_admin_platform_tenant_migrations_imports_create", + "summary": "Create an import job from an age-encrypted archive upload", "tags": [ - "Auth" + "Tenant migration" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChangePassword" + "$ref": "#/components/schemas/ImportCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ChangePassword" + "$ref": "#/components/schemas/ImportCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ChangePassword" + "$ref": "#/components/schemas/ImportCreate" } } }, @@ -49775,197 +50318,174 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChangePasswordResponse" + "$ref": "#/components/schemas/ImportJob" } } }, "description": "" }, "400": { - "description": "Password validation failed." - }, - "401": { - "description": "Authentication credentials were not provided." - } - } - } - }, - "/api/v1/auth/claim/redeem": { - "post": { - "operationId": "api_v1_auth_claim_redeem_create", - "summary": "Redeem a staff-issued member claim code", - "tags": [ - "Auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ClaimRedemption" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ClaimRedemption" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ClaimRedemption" - } - } - }, - "required": true - }, - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ClaimRedemptionResponse" + "$ref": "#/components/schemas/FieldValidationError" } } }, - "description": "" + "description": "Field-keyed validation errors." }, - "400": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "Invalid or expired claim code." - }, - "404": { - "description": "Makerspace not found." - }, - "429": { - "description": "Redemption rate limit exceeded." - } - } - } - }, - "/api/v1/auth/device/attestation-challenge": { - "post": { - "operationId": "api_v1_auth_device_attestation_challenge_create", - "tags": [ - "Device auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceIdentity" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DeviceIdentity" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DeviceIdentity" - } - } + "description": "State conflict." }, - "required": true - }, - "responses": { - "200": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceChallengeResponse" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, - "400": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "503": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } } }, - "/api/v1/auth/device/grants": { + "/api/v1/admin/platform/tenant-migrations/imports/{job_id}": { "get": { - "operationId": "api_v1_auth_device_grants_list", - "tags": [ - "Device auth" - ], - "security": [ + "operationId": "api_v1_admin_platform_tenant_migrations_imports_retrieve", + "summary": "Read a tenant import job", + "parameters": [ { - "jwtAuth": [] - } - ], + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Tenant migration" + ], + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DeviceGrant" - } + "$ref": "#/components/schemas/ImportJob" } } }, "description": "" }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Not found." + }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Superadmin access required." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Throttle limit exceeded." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } } }, - "/api/v1/auth/device/grants/{grant_id}": { - "delete": { - "operationId": "api_v1_auth_device_grants_destroy", + "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/identity-decisions": { + "get": { + "operationId": "api_v1_admin_platform_tenant_migrations_imports_identity_decisions_list", + "summary": "Read the exact archived identity decision list", "parameters": [ { "in": "path", - "name": "grant_id", + "name": "job_id", "schema": { "type": "string", "format": "uuid" @@ -49974,7 +50494,7 @@ } ], "tags": [ - "Device auth" + "Tenant migration" ], "security": [ { @@ -49982,14 +50502,14 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "401": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/ClosureIdentity" + } } } }, @@ -49999,90 +50519,206 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Not found." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Superadmin access required." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Throttle limit exceeded." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } - } - }, - "/api/v1/auth/device/login": { + }, "post": { - "operationId": "api_v1_auth_device_login_create", + "operationId": "api_v1_admin_platform_tenant_migrations_imports_identity_decisions_create", + "summary": "Submit all per-person import identity decisions", + "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "Device auth" + "Tenant migration" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceLogin" + "$ref": "#/components/schemas/ImportDecisionList" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/DeviceLogin" + "$ref": "#/components/schemas/ImportDecisionList" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/DeviceLogin" + "$ref": "#/components/schemas/ImportDecisionList" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceLoginResult" + "$ref": "#/components/schemas/ImportJob" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FieldValidationError" + } + } + }, + "description": "Field-keyed validation errors." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "State conflict." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Not found." + }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "503": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } } }, - "/api/v1/auth/device/logout": { + "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/pairings/{pairing_id}/abort": { "post": { - "operationId": "api_v1_auth_device_logout_create", + "operationId": "api_v1_admin_platform_tenant_migrations_imports_pairings_abort_create", + "summary": "Abort an importing target and issue its signed proof", + "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "pairing_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "Device auth" + "Tenant migration" ], "security": [ { @@ -50094,126 +50730,117 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceLogoutResponse" + "$ref": "#/components/schemas/CutoverOutcome" } } }, "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Not found." }, - "403": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" - } - } - } - }, - "/api/v1/auth/device/refresh": { - "post": { - "operationId": "api_v1_auth_device_refresh_create", - "tags": [ - "Device auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceRefresh" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/DeviceRefresh" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/DeviceRefresh" - } - } + "description": "State conflict." }, - "required": true - }, - "responses": { - "200": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceRefreshResponse" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, - "400": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Superadmin access required." }, - "401": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Throttle limit exceeded." }, - "429": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } } }, - "/api/v1/auth/email-verification/confirm": { + "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/pairings/{pairing_id}/activate": { "post": { - "operationId": "api_v1_auth_email_verification_confirm_create", + "operationId": "api_v1_admin_platform_tenant_migrations_imports_pairings_activate_create", + "summary": "Activate an imported target with the source receipt", + "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "pairing_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "Auth" + "Tenant migration" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmailVerificationConfirm" + "$ref": "#/components/schemas/CutoverReceiptRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/EmailVerificationConfirm" + "$ref": "#/components/schemas/CutoverReceiptRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/EmailVerificationConfirm" + "$ref": "#/components/schemas/CutoverReceiptRequest" } } }, @@ -50229,221 +50856,231 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberVerificationAck" + "$ref": "#/components/schemas/CutoverOutcome" } } }, "description": "" }, "400": { - "description": "Invalid or expired verification code." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." - }, - "429": { - "description": "Request throttled." - } - } - } - }, - "/api/v1/auth/email-verification/resend": { - "post": { - "operationId": "api_v1_auth_email_verification_resend_create", - "tags": [ - "Auth" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberVerificationAck" + "$ref": "#/components/schemas/FieldValidationError" } } }, - "description": "" - }, - "400": { - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." + "description": "Field-keyed validation errors." }, - "429": { - "description": "Request throttled." - } - } - } - }, - "/api/v1/auth/forgot-password": { - "post": { - "operationId": "api_v1_auth_forgot_password_create", - "summary": "Request an emailed password reset code", - "tags": [ - "Auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ForgotPasswordRequest" + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ForgotPasswordRequest" + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ForgotPasswordRequest" - } - } + "description": "State conflict." }, - "required": true - }, - "responses": { - "200": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PasswordResetAcknowledgement" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Authentication required." }, - "400": { - "description": "Invalid request." + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Superadmin access required." }, "429": { - "description": "Request throttled." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Throttle limit exceeded." }, - "503": { + "500": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecoveryUnavailable" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" + "description": "Unexpected migration failure." } } } }, - "/api/v1/auth/login": { + "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/run": { "post": { - "operationId": "api_v1_auth_login_create", - "description": "Takes a set of user credentials and returns an access and refresh JSON web\ntoken pair to prove the authentication of those credentials.", - "summary": "Log in with a password", + "operationId": "api_v1_admin_platform_tenant_migrations_imports_run_create", + "summary": "Run an identity-decided tenant import", + "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "Auth" + "Tenant migration" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LoginRequest" - }, - "examples": { - "StaffLogin": { - "value": { - "username": "admin", - "password": "secret-password", - "surface": "staff" - }, - "summary": "Staff login" - } + "$ref": "#/components/schemas/ImportRun" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/LoginRequest" + "$ref": "#/components/schemas/ImportRun" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/LoginRequest" + "$ref": "#/components/schemas/ImportRun" } } - }, - "required": true + } }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { - "200": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PasswordLoginResult" + "$ref": "#/components/schemas/ImportJob" } } }, "description": "" }, "400": { - "description": "Invalid request." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FieldValidationError" + } + } + }, + "description": "Field-keyed validation errors." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "State conflict." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Not found." + }, + "503": { + "description": "Import worker unavailable." }, "401": { - "description": "Invalid credentials or inactive account." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Account access is restricted." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Superadmin access required." }, "429": { - "description": "Request throttled." - }, - "503": { - "description": "Authentication write boundary unavailable." - } - } - } - }, - "/api/v1/auth/logout": { - "post": { - "operationId": "api_v1_auth_logout_create", - "summary": "Log out and clear refresh cookie", - "tags": [ - "Auth" - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LogoutResponse" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" - }, - "401": { - "description": "Refresh token could not be blacklisted." + "description": "Throttle limit exceeded." }, - "403": { - "description": "CSRF check failed." + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } } }, - "/api/v1/auth/me": { + "/api/v1/admin/platform/tenant-migrations/imports/{job_id}/verification": { "get": { - "operationId": "api_v1_auth_me_retrieve", - "summary": "Get current staff profile", + "operationId": "api_v1_admin_platform_tenant_migrations_imports_verification_retrieve", + "summary": "Read the import verification report", + "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "Auth" + "Tenant migration" ], "security": [ { @@ -50455,84 +51092,81 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AuthUserPayload" + "$ref": "#/components/schemas/VerificationReport" } } }, "description": "" }, - "400": { - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." - }, - "429": { - "description": "Request throttled." - } - } - } - }, - "/api/v1/auth/member-sign-up": { - "post": { - "operationId": "api_v1_auth_member_sign_up_create", - "tags": [ - "Auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MemberSignUp" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MemberSignUp" + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MemberSignUp" - } - } + "description": "Not found." }, - "required": true - }, - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberVerificationAck" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" - }, - "400": { - "description": "Invalid details." + "description": "State conflict." }, "401": { - "description": "Authentication failed." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Permission denied." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Superadmin access required." }, "429": { - "description": "Request throttled." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Throttle limit exceeded." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } } }, - "/api/v1/auth/mfa": { + "/api/v1/admin/platform/tenant-migrations/pairings": { "get": { - "operationId": "api_v1_auth_mfa_retrieve", + "operationId": "api_v1_admin_platform_tenant_migrations_pairings_list", + "summary": "List pinned migration pairings", "tags": [ - "MFA" + "Tenant migration" ], "security": [ { @@ -50544,43 +51178,78 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MFAStatus" + "type": "array", + "items": { + "$ref": "#/components/schemas/Pairing" + } } } }, "description": "" }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Superadmin access required." }, "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Throttle limit exceeded." + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } }, "post": { - "operationId": "api_v1_auth_mfa_create", + "operationId": "api_v1_admin_platform_tenant_migrations_pairings_create", + "summary": "Approve and pin source/target deployment identities", "tags": [ - "MFA" + "Tenant migration" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MFAPassword" + "$ref": "#/components/schemas/PairingCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MFAPassword" + "$ref": "#/components/schemas/PairingCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MFAPassword" + "$ref": "#/components/schemas/PairingCreate" } } }, @@ -50592,115 +51261,96 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MFAChallengeResponse" + "$ref": "#/components/schemas/Pairing" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "503": { - "description": "A required dependency is unavailable." - } - } - } - }, - "/api/v1/auth/mfa/confirm": { - "post": { - "operationId": "api_v1_auth_mfa_confirm_create", - "tags": [ - "MFA" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MFAVerify" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MFAVerify" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FieldValidationError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MFAVerify" - } - } + "description": "Field-keyed validation errors." }, - "required": true - }, - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MFACompleted" + "$ref": "#/components/schemas/TypedError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "description": "State conflict." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Superadmin access required." }, "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Throttle limit exceeded." }, - "503": { - "description": "A required dependency is unavailable." + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TypedError" + } + } + }, + "description": "Unexpected migration failure." } } } }, - "/api/v1/auth/mfa/disable": { - "post": { - "operationId": "api_v1_auth_mfa_disable_create", + "/api/v1/admin/printer-jobs/{id}": { + "get": { + "operationId": "api_v1_admin_printer_jobs_retrieve", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "MFA" + "Devices" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MFAPassword" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MFAPassword" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MFAPassword" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -50711,222 +51361,223 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MFAStatus" + "$ref": "#/components/schemas/Job" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Invalid payload or adapter module disabled." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authentication/signature/replay refused." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authority or device revoked." }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Target outside scope or absent." }, - "503": { - "description": "A required dependency is unavailable." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Conflicting command/report." + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Delivery lease expired." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/auth/mfa/enroll": { + "/api/v1/admin/printer-jobs/{id}/apply": { "post": { - "operationId": "api_v1_auth_mfa_enroll_create", + "operationId": "api_v1_admin_printer_jobs_apply_create", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "MFA" + "Devices" + ], + "security": [ + { + "jwtAuth": [] + } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MFAChallenge" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MFAChallenge" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MFAChallenge" - } - } - }, - "required": true - }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MFAEnrollment" + "$ref": "#/components/schemas/Job" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "503": { - "description": "A required dependency is unavailable." - } - } - } - }, - "/api/v1/auth/mfa/recovery": { - "post": { - "operationId": "api_v1_auth_mfa_recovery_create", - "tags": [ - "MFA" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MFAPassword" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MFAPassword" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MFAPassword" - } - } + "description": "Invalid payload or adapter module disabled." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MFARecovery" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." + "description": "Authentication/signature/replay refused." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "503": { - "description": "A required dependency is unavailable." - } - } - } - }, - "/api/v1/auth/mfa/verify": { - "post": { - "operationId": "api_v1_auth_mfa_verify_create", - "tags": [ - "MFA" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MFAVerify" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MFAVerify" + "description": "Authority or device revoked." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MFAVerify" - } - } + "description": "Target outside scope or absent." }, - "required": true - }, - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MFACompleted" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Conflicting command/report." }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Delivery lease expired." }, - "503": { - "description": "A required dependency is unavailable." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/auth/organization-invitations/redeem/": { + "/api/v1/admin/products/{id}/assets/generate": { "post": { - "operationId": "api_v1_auth_organization_invitations_redeem_create", - "summary": "Redeem a single-use organization invitation", + "operationId": "api_v1_admin_products_assets_generate_create", + "summary": "Generate asset units", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Auth" + "Asset units" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationRedeem" + "$ref": "#/components/schemas/AssetGenerate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationRedeem" + "$ref": "#/components/schemas/AssetGenerate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationRedeem" + "$ref": "#/components/schemas/AssetGenerate" } } }, @@ -50938,62 +51589,140 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OrganizationInvitationRedeemed" + "$ref": "#/components/schemas/AssetGenerateResult" } } }, "description": "" }, "400": { - "description": "Malformed token." + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "description": "Invalid input, disabled module, or non-draft print batch." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] } } }, - "description": "" + "description": "QR management permission required." }, "404": { - "description": "Invitation not found." + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "description": "Batch or inventory target not in scope." }, - "409": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] } } }, - "description": "" + "description": "Tenant frozen for migration." } } } }, - "/api/v1/auth/phone": { - "delete": { - "operationId": "api_v1_auth_phone_destroy", - "description": "Detach the number. Always safe: every account keeps an email credential.", - "summary": "Unlink the phone number", + "/api/v1/admin/qr-print-batches/{id}": { + "get": { + "operationId": "api_v1_admin_qr_print_batches_retrieve", + "summary": "Retrieve QR print batch", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Auth" + "QR print batches" ], "security": [ { @@ -51005,49 +51734,32 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PhoneStatus" + "$ref": "#/components/schemas/QrPrintBatchDetail" } } }, "description": "" - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." } } } }, - "/api/v1/auth/phone/link/confirm": { - "post": { - "operationId": "api_v1_auth_phone_link_confirm_create", - "description": "Attach the verified number to the caller's account.", - "summary": "Confirm and link a phone number", + "/api/v1/admin/qr-print-batches/{id}/download": { + "get": { + "operationId": "api_v1_admin_qr_print_batches_download_retrieve", + "summary": "Download QR print batch as a ZIP of captioned SVGs", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Auth" + "QR print batches" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PhoneConfirm" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PhoneConfirm" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PhoneConfirm" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -51056,46 +51768,50 @@ "responses": { "200": { "content": { - "application/json": { + "application/zip": { "schema": { - "$ref": "#/components/schemas/PhoneStatus" + "type": "string", + "format": "binary" } } }, "description": "" - }, - "400": { - "description": "Invalid or expired code." - }, - "429": { - "description": "Request throttled." } } } }, - "/api/v1/auth/phone/link/start": { + "/api/v1/admin/qr-print-batches/{id}/items": { "post": { - "operationId": "api_v1_auth_phone_link_start_create", - "description": "Send a code to a number the caller wants to attach to their own account.", - "summary": "Request a code to link a phone number", + "operationId": "api_v1_admin_qr_print_batches_items_create", + "summary": "Add QR code to draft print batch", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Auth" + "QR print batches" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PhoneStart" + "$ref": "#/components/schemas/QrPrintBatchItemCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PhoneStart" + "$ref": "#/components/schemas/QrPrintBatchItemCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PhoneStart" + "$ref": "#/components/schemas/QrPrintBatchItemCreate" } } }, @@ -51107,719 +51823,869 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PhoneStartResponse" + "$ref": "#/components/schemas/QrPrintBatchItemResult" } } }, "description": "" }, "400": { - "description": "Invalid or already-linked number." - }, - "404": { - "description": "Phone sign-in is not configured." - }, - "429": { - "description": "Request throttled." - } - } - } - }, - "/api/v1/auth/phone/login/confirm": { - "post": { - "operationId": "api_v1_auth_phone_login_confirm_create", - "description": "Exchange a valid code for a MEMBER session. Never mints a staff session.", - "summary": "Sign in with a phone code", - "tags": [ - "Auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PhoneConfirm" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PhoneConfirm" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PhoneConfirm" - } - } - }, - "required": true - }, - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PhoneLoginResult" + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] } } }, - "description": "" + "description": "Invalid input, disabled module, or non-draft print batch." }, "401": { - "description": "The first-factor account is no longer available." - }, - "400": { - "description": "Invalid or expired code." - }, - "404": { - "description": "Phone sign-in is not configured." - }, - "429": { - "description": "Request throttled." - }, - "503": { - "description": "Authentication write boundary unavailable." - } - } - } - }, - "/api/v1/auth/phone/login/start": { - "post": { - "operationId": "api_v1_auth_phone_login_start_create", - "description": "Request a login code for an already-verified number.", - "summary": "Request a phone sign-in code", - "tags": [ - "Auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PhoneStart" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PhoneStart" + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PhoneStart" - } - } + "description": "Authentication required." }, - "required": true - }, - "responses": { - "200": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PhoneStartResponse" + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] } } }, - "description": "" - }, - "400": { - "description": "Invalid request." + "description": "QR management permission required." }, "404": { - "description": "Phone sign-in is not configured." - }, - "429": { - "description": "Request throttled." - } - } - } - }, - "/api/v1/auth/refresh": { - "post": { - "operationId": "api_v1_auth_refresh_create", - "description": "Takes a refresh type JSON web token and returns an access type JSON web\ntoken if the refresh token is valid.", - "summary": "Refresh access token", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "description": "Batch or inventory target not in scope." + }, + "423": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "description": "Tenant frozen for migration." + } + } + } + }, + "/api/v1/admin/qr-print-batches/{id}/label-commands": { + "get": { + "operationId": "api_v1_admin_qr_print_batches_label_commands_list", + "summary": "List label commands for a QR batch", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + } + ], "tags": [ - "Auth" + "Devices" + ], + "security": [ + { + "jwtAuth": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RefreshResponse" + "$ref": "#/components/schemas/PaginatedLabelCommandList" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "description": "DRF field/module validation errors or non-field validation messages." + }, "401": { - "description": "Missing, invalid, or replayed refresh token." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authentication required or invalid signature." }, "403": { - "description": "CSRF check failed or account restricted." - } - } - } - }, - "/api/v1/auth/reset-password": { - "post": { - "operationId": "api_v1_auth_reset_password_create", - "summary": "Confirm an OTP or coexisting legacy password reset link", - "tags": [ - "Auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ResetPasswordConfirmRequest" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ResetPasswordConfirmRequest" + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ResetPasswordConfirmRequest" - } - } - } - }, - "responses": { - "200": { + "description": "Command or batch not in scope." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PasswordUpdated" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Idempotency or delivery conflict." }, - "400": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResetPasswordConfirmError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Batch no longer available." }, - "429": { - "description": "Request throttled." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } - } - }, - "/api/v1/auth/social/apple": { + }, "post": { - "operationId": "api_v1_auth_social_apple_create", + "operationId": "api_v1_admin_qr_print_batches_label_commands_create", + "summary": "Send or reprint a QR batch", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Social auth" + "Devices" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/LabelCreate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/LabelCreate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/LabelCreate" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialLoginResponse" + "$ref": "#/components/schemas/LabelCommand" + } + } + }, + "description": "" + }, + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LabelCommand" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + }, + "description": "DRF field/module validation errors or non-field validation messages." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authentication required or invalid signature." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Command or batch not in scope." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Includes `social_device_restart_required` when burned pre-grant inputs must be replaced." + "description": "Idempotency or delivery conflict." }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Batch no longer available." }, - "503": { - "description": "A required dependency is unavailable." + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/auth/social/github/authorize": { + "/api/v1/admin/qr/{id}/print": { + "get": { + "operationId": "api_v1_admin_qr_print_retrieve", + "summary": "Render QR label SVG", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "QR assets" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "description": "SVG QR label." + } + } + } + }, + "/api/v1/admin/qr/{id}/rebind-target": { "post": { - "operationId": "api_v1_auth_social_github_authorize_create", + "operationId": "api_v1_admin_qr_rebind_target_create", + "summary": "Rebind a saved QR to another product/asset (cross-makerspace = superadmin) and optionally rename", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Social auth" + "QR assets" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GithubBrowserStart" + "$ref": "#/components/schemas/QrRebindTarget" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/GithubBrowserStart" + "$ref": "#/components/schemas/QrRebindTarget" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/GithubBrowserStart" + "$ref": "#/components/schemas/QrRebindTarget" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GithubBrowserStartResponse" + "$ref": "#/components/schemas/QrRebindResult" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "503": { - "description": "A required dependency is unavailable." + "409": { + "description": "Outstanding loan or target QR conflict." } } } }, - "/api/v1/auth/social/github/callback": { + "/api/v1/admin/qr/{id}/revoke": { "post": { - "operationId": "api_v1_auth_social_github_callback_create", + "operationId": "api_v1_admin_qr_revoke_create", + "summary": "Revoke active QR code", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Social auth" + "QR assets" + ], + "security": [ + { + "jwtAuth": [] + } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OidcBrowserCallback" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/OidcBrowserCallback" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/OidcBrowserCallback" - } - } - }, - "required": true - }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OidcBrowserLoginResponse" + "$ref": "#/components/schemas/QrCode" } } }, "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "409": { - "description": "Conflicts with the current state." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "503": { - "description": "A required dependency is unavailable." } } } }, - "/api/v1/auth/social/google": { + "/api/v1/admin/qr/boxes": { "post": { - "operationId": "api_v1_auth_social_google_create", + "operationId": "api_v1_admin_qr_boxes_create", + "summary": "Create a QR-coded box", "tags": [ - "Social auth" + "QR assets" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/CreateBoxQr" + }, + "examples": { + "CreateAQR-codedBox": { + "value": { + "makerspace_id": 1, + "label": "Electronics Box A", + "location": "Bench Storage", + "description": "Issued hardware kit box" + }, + "summary": "Create a QR-coded box" + } } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/CreateBoxQr" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/CreateBoxQr" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialLoginResponse" + "$ref": "#/components/schemas/Box" } } }, "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Includes `social_device_restart_required` when burned pre-grant inputs must be replaced." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "503": { - "description": "A required dependency is unavailable." } } } }, - "/api/v1/auth/social/nonce": { + "/api/v1/admin/qr/containers": { "post": { - "operationId": "api_v1_auth_social_nonce_create", + "operationId": "api_v1_admin_qr_containers_create", + "summary": "Create a QR-coded box", "tags": [ - "Social auth" + "QR assets" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialNonce" + "$ref": "#/components/schemas/CreateBoxQr" + }, + "examples": { + "CreateAQR-codedBox": { + "value": { + "makerspace_id": 1, + "label": "Electronics Box A", + "location": "Bench Storage", + "description": "Issued hardware kit box" + }, + "summary": "Create a QR-coded box" + } } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/SocialNonce" + "$ref": "#/components/schemas/CreateBoxQr" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/SocialNonce" + "$ref": "#/components/schemas/CreateBoxQr" } } }, "required": true }, - "responses": { - "200": { + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialNonceResponse" + "$ref": "#/components/schemas/Box" } } }, "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." } } } }, - "/api/v1/auth/social/oidc/{slug}": { + "/api/v1/admin/qr/resolve": { "post": { - "operationId": "api_v1_auth_social_oidc_create", - "description": "Login through a deployment-configured OIDC provider named in the URL.\n\nThe slug is resolved to a configured row on every request rather than captured at\nimport time, so disabling a provider takes effect immediately instead of at the next\nrestart -- the same reason `provider_for_slug` refuses disabled and half-filled rows.", - "parameters": [ - { - "in": "path", - "name": "slug", - "schema": { - "type": "string" - }, - "required": true - } - ], + "operationId": "api_v1_admin_qr_resolve_create", + "description": "Resolve the opaque payload encoded in a physical QR label to its target (box, product, or asset) and the scanner actions the caller may take. `payload` is the raw 32-char lowercase hex token printed on the label (Python `uuid4().hex`, e.g. `3f9a1c2b4d5e6f7081920a1b2c3d4e5f`) - it is the value stored as `QrCode.payload` (and `Box.code` for boxes), not a URL or JSON. Resolving a QR also records an immutable scanner-lookup scan event.", + "summary": "Resolve QR target and scanner allowed actions", "tags": [ - "Social auth" + "QR assets" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/QrResolve" + }, + "examples": { + "ResolveAScannedQRPayload": { + "value": { + "payload": "3f9a1c2b4d5e6f7081920a1b2c3d4e5f" + }, + "summary": "Resolve a scanned QR payload" + } } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/QrResolve" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/SocialLogin" + "$ref": "#/components/schemas/QrResolve" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialLoginResponse" + "$ref": "#/components/schemas/QrResolveResult" + }, + "examples": { + "ResolvedBoxQR": { + "value": { + "qr": { + "id": 12, + "makerspace": 1, + "payload": "3f9a1c2b4d5e6f7081920a1b2c3d4e5f", + "target_type": "box", + "target_id": 5, + "status": "active", + "created_at": "2026-06-27T09:30:00Z", + "updated_at": "2026-06-27T09:30:00Z", + "revoked_at": null + }, + "target": { + "type": "box", + "id": 5, + "label": "Electronics Box A", + "code": "3f9a1c2b4d5e6f7081920a1b2c3d4e5f" + }, + "allowed_actions": [ + "contents", + "move_container", + "record_scan", + "revoke", + "view" + ] + }, + "summary": "Resolved box QR" + } } } }, "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Includes `social_device_restart_required` when burned pre-grant inputs must be replaced." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "503": { - "description": "A required dependency is unavailable." } } } }, - "/api/v1/auth/social/oidc/{slug}/authorize": { + "/api/v1/admin/qr/scan": { "post": { - "operationId": "api_v1_auth_social_oidc_authorize_create", - "parameters": [ - { - "in": "path", - "name": "slug", - "schema": { - "type": "string" - }, - "required": true - } - ], + "operationId": "api_v1_admin_qr_scan_create", + "description": "Context is limited to issue or return and scan events are immutable.", + "summary": "Record a QR scan", "tags": [ - "Social auth" + "QR assets" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OidcBrowserStart" + "$ref": "#/components/schemas/QrScan" + }, + "examples": { + "ScanQRDuringIssue": { + "value": { + "payload": "3f9a1c2b4d5e6f7081920a1b2c3d4e5f", + "context": "issue", + "request_id": 99 + }, + "summary": "Scan QR during issue" + } } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/OidcBrowserStart" + "$ref": "#/components/schemas/QrScan" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/OidcBrowserStart" + "$ref": "#/components/schemas/QrScan" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OidcBrowserStartResponse" + "$ref": "#/components/schemas/QrScanResult" } } }, "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + } + } + } + }, + "/api/v1/admin/qr/tools": { + "post": { + "operationId": "api_v1_admin_qr_tools_create", + "summary": "Create or reuse a QR code for a product or asset", + "tags": [ + "QR assets" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateToolQr" } }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CreateToolQr" } }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CreateToolQr" } - }, - "description": "" + } }, - "503": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/QrCode" } } }, "description": "" - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." } } } }, - "/api/v1/auth/social/oidc/callback": { - "post": { - "operationId": "api_v1_auth_social_oidc_callback_create", + "/api/v1/admin/report-schedules/{id}": { + "patch": { + "operationId": "api_v1_admin_report_schedules_partial_update", + "summary": "Update report schedule", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Social auth" + "Reports" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OidcBrowserCallback" + "$ref": "#/components/schemas/PatchedReportSchedule" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/OidcBrowserCallback" + "$ref": "#/components/schemas/PatchedReportSchedule" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/OidcBrowserCallback" + "$ref": "#/components/schemas/PatchedReportSchedule" } } - }, - "required": true + } }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OidcBrowserLoginResponse" + "$ref": "#/components/schemas/ReportSchedule" } } }, @@ -51829,63 +52695,79 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid schedule or module disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, - "409": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Makerspace or schedule not found." }, - "503": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Tenant writes are frozen." }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Tenant write gate unavailable." } } - } - }, - "/api/v1/auth/social/providers": { - "get": { - "operationId": "api_v1_auth_social_providers_list", + }, + "delete": { + "operationId": "api_v1_admin_report_schedules_destroy", + "summary": "Delete report schedule", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Social auth" + "Reports" ], "security": [ { @@ -51893,104 +52775,88 @@ } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "400": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SocialIdentity" - } + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Invalid schedule or module disabled." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - } - } - }, - "post": { - "operationId": "api_v1_auth_social_providers_create", - "tags": [ - "Social auth" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SocialLink" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/SocialLink" + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/SocialLink" - } - } + "description": "Makerspace or schedule not found." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SocialIdentity" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "409": { - "description": "Conflicts with the current state." + "description": "Tenant writes are frozen." }, "503": { - "description": "A required dependency is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Tenant write gate unavailable." } } } }, - "/api/v1/auth/social/providers/{provider}": { - "delete": { - "operationId": "api_v1_auth_social_providers_destroy", + "/api/v1/admin/report-schedules/{id}/run-now": { + "post": { + "operationId": "api_v1_admin_report_schedules_run_now_create", + "summary": "Run a report schedule now", "parameters": [ { "in": "path", - "name": "provider", + "name": "id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "Social auth" + "Reports" ], "security": [ { @@ -51998,287 +52864,435 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "409": { - "description": "Conflicts with the current state." - } - } - } - }, - "/api/v1/auth/staff-invitations/accept": { - "post": { - "operationId": "api_v1_auth_staff_invitations_accept_create", - "tags": [ - "Staff invitations" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/StaffInvitationAccept" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/StaffInvitationAccept" + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportDelivery" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/StaffInvitationAccept" - } - } + "description": "" }, - "required": true - }, - "responses": { - "200": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StaffInvitationAccepted" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Schedule inactive or skipped." }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Invalid schedule or module disabled." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or schedule not found." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Tenant writes are frozen." }, "503": { - "description": "A required dependency is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Tenant write gate unavailable." } } } }, - "/api/v1/backups/download/{archive_id}/{token}": { + "/api/v1/admin/reports/{report_key}/export": { "get": { - "operationId": "api_v1_backups_download_retrieve", - "summary": "Consume a one-use backup download", + "operationId": "api_v1_admin_reports_export_retrieve", + "summary": "Export aggregate report", "parameters": [ { - "in": "path", - "name": "archive_id", + "in": "query", + "name": "end", "schema": { "type": "string", - "format": "uuid" + "format": "date" + } + }, + { + "in": "query", + "name": "format", + "schema": { + "type": "string", + "enum": [ + "csv", + "xlsx" + ] + } + }, + { + "in": "query", + "name": "grain", + "schema": { + "type": "string", + "enum": [ + "day", + "month", + "week" + ] + } + }, + { + "in": "query", + "name": "months", + "schema": { + "type": "integer" }, - "required": true + "description": "Retention anniversary in months (1-120); default 3, 6 and 12." }, { "in": "path", - "name": "token", + "name": "report_key", "schema": { - "type": "string" + "type": "string", + "enum": [ + "asset-usage", + "audit-activity", + "booking-lead-time", + "booking-occupancy", + "booking-utilization", + "cash-book", + "certification-coverage", + "communications-health", + "community-engagement", + "consumable-pools", + "consumable-usage", + "damaged-lost", + "damaged-missing", + "device-health", + "event-attendance", + "event-checkin-times", + "event-feedback-scores", + "event-funnel", + "event-revenue", + "event-signups", + "evidence-compliance", + "fablab-health", + "import-quality", + "inventory-control", + "inventory-utilisation", + "inventory-watch-demand", + "loan-throughput", + "machine-downtime", + "machine-errors", + "machine-service", + "machine-usage", + "machine-usage-heatmap", + "maintenance-activity", + "member-activity", + "member-retention", + "membership-growth", + "membership-revenue", + "mobile-auth", + "mobile-devices", + "module-operational-health", + "most-lent", + "organization-membership", + "payment-cash-book", + "payment-reconciliation", + "payments-ageing", + "platform-services", + "platform-usage", + "presence-activity", + "printer-jobs", + "printer-service", + "problem-reports", + "procurement-performance", + "qr-scans", + "recently-added", + "request-funnel", + "revenue-over-time", + "service-queue-times", + "stock-health", + "stock-transfers", + "stocktake-variance", + "summary", + "taken-items", + "warranty-expiry" + ] }, "required": true - } - ], - "tags": [ - "Backup" - ], - "responses": { - "200": { - "description": "Age-encrypted archive stream." }, - "404": { - "description": "Invalid or expired download." - } - } - } - }, - "/api/v1/bootstrap": { - "get": { - "operationId": "api_v1_bootstrap_retrieve", - "summary": "Resolve tenant and frontend-safe configuration", - "parameters": [ { "in": "query", - "name": "slug", + "name": "start", "schema": { - "type": "string" + "type": "string", + "format": "date" } }, { "in": "query", - "name": "tenant", + "name": "status", "schema": { - "type": "string" + "type": "string", + "enum": [ + "canceled", + "paid_offline", + "paid_online", + "pending", + "waived" + ] + } + }, + { + "in": "query", + "name": "subject_type", + "schema": { + "type": "string", + "enum": [ + "booking", + "event_registration", + "loan_deposit", + "loan_late_fee", + "machine_service_request", + "makerspace_membership", + "membership_request", + "membership_term" + ] } } ], "tags": [ - "Tenant bootstrap" + "Reports" ], "security": [ { "jwtAuth": [] - }, - {} + } ], "responses": { + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Render rate limit exceeded." + }, "200": { + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "text/csv": { + "schema": { + "type": "string" + } + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TenantBootstrap" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "Frontend-safe tenant bootstrap payload." + "description": "Invalid report request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "No active tenant frontend matched." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/config": { + "/api/v1/admin/reports/catalog": { "get": { - "operationId": "api_v1_config_retrieve", - "summary": "Return frontend-safe platform configuration", + "operationId": "api_v1_admin_reports_catalog_retrieve", + "summary": "List deployment report catalog", "tags": [ - "Platform" + "Reports" ], "security": [ - {} + { + "jwtAuth": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicConfig" + "$ref": "#/components/schemas/ReportCatalog" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "503": { - "description": "A required dependency is unavailable." - } - } - } - }, - "/api/v1/data-exports/download/{job_id}/{token}": { - "get": { - "operationId": "api_v1_data_exports_download_retrieve", - "summary": "Consume a one-use export download URL", - "parameters": [ - { - "in": "path", - "name": "job_id", - "schema": { - "type": "string", - "format": "uuid" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } }, - "required": true + "description": "Invalid report request." }, - { - "in": "path", - "name": "token", - "schema": { - "type": "string" - }, - "required": true - } - ], - "tags": [ - "Data exports" - ], - "responses": { - "200": { + "401": { "content": { - "application/zip": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ReportError" } - }, - "application/octet-stream": { + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ReportError" } } }, - "description": "" + "description": "Permission denied." }, "404": { - "description": "Download link is invalid, expired, or used." - }, - "503": { - "description": "Archive storage is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReportError" + } + } + }, + "description": "Makerspace or report not found." } } } }, - "/api/v1/device/commands/{id}/ack": { + "/api/v1/admin/requests/{id}/accept": { "post": { - "operationId": "api_v1_device_commands_ack_create", + "operationId": "api_v1_admin_requests_accept_create", + "summary": "Accept borrow request", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Device protocol" + "Admin requests" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Ack" + "$ref": "#/components/schemas/AcceptRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Ack" + "$ref": "#/components/schemas/AcceptRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Ack" + "$ref": "#/components/schemas/AcceptRequest" } } - }, - "required": true + } }, "security": [ { - "DeviceSignature": [] + "jwtAuth": [] } ], "responses": { @@ -52286,7 +53300,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CommandAck" + "$ref": "#/components/schemas/AdminRequest" } } }, @@ -52296,84 +53310,95 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid payload or adapter module disabled." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication/signature/replay refused." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authority or device revoked." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Target outside scope or absent." + "description": "Not found." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Conflicting command/report." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Delivery lease expired." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Workflow conflict." } } } }, - "/api/v1/device/commands/lease": { + "/api/v1/admin/requests/{id}/assign-box": { "post": { - "operationId": "api_v1_device_commands_lease_create", + "operationId": "api_v1_admin_requests_assign_box_create", + "summary": "Assign box to accepted request", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Device protocol" + "Admin requests" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssignBox" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/AssignBox" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/AssignBox" + } + } + }, + "required": true + }, "security": [ { - "DeviceSignature": [] + "jwtAuth": [] } ], "responses": { @@ -52381,7 +53406,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LeaseResponse" + "$ref": "#/components/schemas/AdminRequest" } } }, @@ -52391,119 +53416,103 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid payload or adapter module disabled." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication/signature/replay refused." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authority or device revoked." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Target outside scope or absent." + "description": "Not found." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Conflicting command/report." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Delivery lease expired." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Workflow conflict." } } } }, - "/api/v1/device/enrollments/{id}/challenge": { + "/api/v1/admin/requests/{id}/issue": { "post": { - "operationId": "api_v1_device_enrollments_challenge_create", - "summary": "Request a proof-of-possession challenge for a candidate key", + "operationId": "api_v1_admin_requests_issue_create", + "summary": "Issue accepted request", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Device protocol" + "Admin requests" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChallengeRequest" + "$ref": "#/components/schemas/IssueRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ChallengeRequest" + "$ref": "#/components/schemas/IssueRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ChallengeRequest" + "$ref": "#/components/schemas/IssueRequest" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChallengeResponse" + "$ref": "#/components/schemas/AdminRequest" } } }, @@ -52513,37 +53522,37 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Malformed key, challenge or signature, or module disabled." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not authenticated." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Enrollment credentials or proof of possession rejected." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -52553,79 +53562,73 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The key is already bound to another device, or rotation reused the key." + "description": "Workflow conflict." }, - "410": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "The enrollment was used, revoked or has expired." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Tenant frozen for migration." + "description": "Service unavailable." } } } }, - "/api/v1/device/enrollments/{id}/complete": { + "/api/v1/admin/requests/{id}/reject": { "post": { - "operationId": "api_v1_device_enrollments_complete_create", - "summary": "Complete enrollment by signing the challenge with the candidate key", + "operationId": "api_v1_admin_requests_reject_create", + "summary": "Reject borrow request", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Device protocol" + "Admin requests" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CompleteRequest" + "$ref": "#/components/schemas/RejectRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CompleteRequest" + "$ref": "#/components/schemas/RejectRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CompleteRequest" + "$ref": "#/components/schemas/RejectRequest" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ConnectedDeviceIdentity" + "$ref": "#/components/schemas/AdminRequest" } } }, @@ -52635,37 +53638,37 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Malformed key, challenge or signature, or module disabled." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Not authenticated." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Enrollment credentials or proof of possession rejected." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -52675,131 +53678,63 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "The key is already bound to another device, or rotation reused the key." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "The enrollment was used, revoked or has expired." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Workflow conflict." } } } }, - "/api/v1/device/heartbeat": { + "/api/v1/admin/requests/{id}/return": { "post": { - "operationId": "api_v1_device_heartbeat_create", - "summary": "Report device activity and recover an offline device (signed request)", - "tags": [ - "Device protocol" - ], - "security": [ - { - "DeviceSignature": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ConnectedDeviceIdentity" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Module disabled." - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Missing, malformed, stale, replayed or invalid signature." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Device not allowed on this route, revoked or tenant unavailable." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Tenant frozen for migration." - } - } - } - }, - "/api/v1/device/label-commands/{id}/content": { - "get": { - "operationId": "api_v1_device_label_commands_content_retrieve", - "summary": "Download acknowledged label ZIP", + "operationId": "api_v1_admin_requests_return_create", + "summary": "Return issued request items", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Device protocol" + "Admin requests" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReturnRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ReturnRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ReturnRequest" + } + } + }, + "required": true + }, "security": [ { - "DeviceSignature": [] + "jwtAuth": [] } ], "responses": { "200": { "content": { - "application/zip": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/AdminRequest" } } }, @@ -52809,38 +53744,27 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "DRF field/module validation errors or non-field validation messages." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required or invalid signature." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -52850,78 +53774,67 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Command or batch not in scope." + "description": "Not found." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Idempotency or delivery conflict." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Batch no longer available." + "description": "Workflow conflict." }, - "423": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Service unavailable." } } } }, - "/api/v1/device/label-commands/{id}/result": { + "/api/v1/admin/requests/{id}/return-due": { "post": { - "operationId": "api_v1_device_label_commands_result_create", - "summary": "Report an idempotent label result", + "operationId": "api_v1_admin_requests_return_due_create", + "summary": "Set request return due time", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Device protocol" + "Admin requests" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LabelResult" + "$ref": "#/components/schemas/ReturnDue" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/LabelResult" + "$ref": "#/components/schemas/ReturnDue" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/LabelResult" + "$ref": "#/components/schemas/ReturnDue" } } }, @@ -52929,7 +53842,7 @@ }, "security": [ { - "DeviceSignature": [] + "jwtAuth": [] } ], "responses": { @@ -52937,7 +53850,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LabelReceipt" + "$ref": "#/components/schemas/AdminRequest" } } }, @@ -52947,38 +53860,27 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "DRF field/module validation errors or non-field validation messages." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication required or invalid signature." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -52988,85 +53890,54 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Command or batch not in scope." + "description": "Not found." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Idempotency or delivery conflict." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Batch no longer available." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Workflow conflict." } } } }, - "/api/v1/device/printer-jobs/{id}/result": { - "post": { - "operationId": "api_v1_device_printer_jobs_result_create", + "/api/v1/admin/requests/{id}/timeline": { + "get": { + "operationId": "api_v1_admin_requests_timeline_retrieve", + "description": "Scans, evidence, asset outcomes, accountability, problem reports and related audit rows (`kind=audit`; metadata is the presentation-catalogue projection), ordered by (at, kind, source id). Events with an `evidence_id` carry `evidence_retention` (`null` = no retention-state row, not proof the bytes exist). No object key or signed URL is ever returned; photo bytes stay behind the UPLOAD_EVIDENCE-gated evidence endpoint.", + "summary": "Read-only immutable timeline for one hardware request", "parameters": [ { "in": "path", "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + }, + "description": "Maximum history events to return. Defaults to 200; capped at 500." } ], "tags": [ - "Device protocol" + "Admin requests" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Result" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/Result" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/Result" - } - } - }, - "required": true - }, "security": [ { - "DeviceSignature": [] + "jwtAuth": [] } ], "responses": { @@ -53074,7 +53945,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Job" + "$ref": "#/components/schemas/RequestTimelineResponse" } } }, @@ -53084,115 +53955,99 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid payload or adapter module disabled." + "description": "Invalid request." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication/signature/replay refused." + "description": "Authentication required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authority or device revoked." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Target outside scope or absent." + "description": "Not found." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Conflicting command/report." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Delivery lease expired." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "Workflow conflict." } } } }, - "/api/v1/device/printer-jobs/{id}/telemetry": { - "post": { - "operationId": "api_v1_device_printer_jobs_telemetry_create", + "/api/v1/admin/search": { + "get": { + "operationId": "api_v1_admin_search_retrieve", + "description": "At most limit hits per entity (default 5, maximum 10). Members match username; bookings match space name or booking ID. Disabled or unauthorized entity lists contribute no hits.", + "summary": "Search authorized staff lists", "parameters": [ { - "in": "path", - "name": "id", + "in": "query", + "name": "limit", + "schema": { + "type": "integer", + "maximum": 10, + "minimum": 1, + "default": 5 + } + }, + { + "in": "query", + "name": "makerspace_id", + "schema": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 1, + "format": "int64" + }, + "required": true + }, + { + "in": "query", + "name": "q", "schema": { "type": "string", - "format": "uuid" + "maxLength": 100, + "minLength": 2 }, "required": true } ], "tags": [ - "Device protocol" + "Staff lists" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Telemetry" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/Telemetry" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/Telemetry" - } - } - }, - "required": true - }, "security": [ { - "DeviceSignature": [] + "jwtAuth": [] } ], "responses": { @@ -53200,7 +54055,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Job" + "$ref": "#/components/schemas/SearchOutput" } } }, @@ -53210,252 +54065,326 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid payload or adapter module disabled." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authentication/signature/replay refused." + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Authority or device revoked." + "description": "" }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Target outside scope or absent." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" - } - } - }, - "description": "Conflicting command/report." - }, - "410": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Delivery lease expired." + "description": "" }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeviceError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Tenant frozen for migration." + "description": "" } } } }, - "/api/v1/event-checkin-stations/{public_token}/roster/": { + "/api/v1/admin/space-pack-profiles": { "get": { - "operationId": "api_v1_event_checkin_stations_roster_retrieve", - "summary": "Download the station's minimal expiring attendee roster", + "operationId": "api_v1_admin_space_pack_profiles_list", "parameters": [ { - "in": "path", - "name": "public_token", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "type": "integer" + } } ], "tags": [ - "Event check-in stations" + "Platform" ], "security": [ { - "EventStationCookie": [] - }, - {} + "jwtAuth": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OfflineRosterResponse" + "$ref": "#/components/schemas/PaginatedPackProfileList" } } }, "description": "" }, - "403": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "" }, - "409": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "" }, - "413": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "" }, - "429": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" } } - } - }, - "/api/v1/event-checkin-stations/{public_token}/session/": { + }, "post": { - "operationId": "api_v1_event_checkin_stations_session_create", - "summary": "Exchange an event-scoped PIN for a station session", - "parameters": [ - { - "in": "path", - "name": "public_token", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], + "operationId": "api_v1_admin_space_pack_profiles_create", "tags": [ - "Event check-in stations" + "Platform" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StationPin" + "$ref": "#/components/schemas/PackProfile" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/StationPin" + "$ref": "#/components/schemas/PackProfile" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/StationPin" + "$ref": "#/components/schemas/PackProfile" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { - "204": { - "description": "No response body" + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackProfile" + } + } + }, + "description": "" }, "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "" }, - "429": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PackError" } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackError" + } + } + }, + "description": "" } } - }, - "delete": { - "operationId": "api_v1_event_checkin_stations_session_destroy", - "summary": "Clear the local station session cookie", + } + }, + "/api/v1/admin/spaces/{id}/": { + "get": { + "operationId": "api_v1_admin_spaces_retrieve", + "summary": "Retrieve a bookable space", "parameters": [ { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Event check-in stations" + "Admin bookings" ], "security": [ { - "EventStationCookie": [] + "jwtAuth": [] } ], "responses": { - "204": { - "description": "No response body" + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookableSpaceAdmin" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { "content": { @@ -53465,9 +54394,9 @@ } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "Permission denied." }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -53475,61 +54404,56 @@ } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "Not found." } } - } - }, - "/api/v1/event-checkin-stations/{public_token}/sync/": { - "post": { - "operationId": "api_v1_event_checkin_stations_sync_create", - "summary": "Synchronize queued PIN-station check-ins", + }, + "patch": { + "operationId": "api_v1_admin_spaces_partial_update", + "summary": "Update a bookable space", "parameters": [ { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Event check-in stations" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + "$ref": "#/components/schemas/PatchedBookableSpaceWrite" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + "$ref": "#/components/schemas/PatchedBookableSpaceWrite" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + "$ref": "#/components/schemas/PatchedBookableSpaceWrite" } } - }, - "required": true + } }, "security": [ { - "EventStationCookie": [] - }, - {} + "jwtAuth": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OfflineCheckInSyncResponse" + "$ref": "#/components/schemas/BookableSpaceAdmin" } } }, @@ -53543,7 +54467,17 @@ } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { "content": { @@ -53553,9 +54487,9 @@ } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "Permission denied." }, - "410": { + "404": { "content": { "application/json": { "schema": { @@ -53563,9 +54497,9 @@ } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "Not found." }, - "429": { + "409": { "content": { "application/json": { "schema": { @@ -53573,65 +54507,35 @@ } } }, - "description": "Invalid station credential, session, origin, feature, or time window." + "description": "Workflow conflict." } } } }, - "/api/v1/guest-admin/makerspace/{makerspace_id}/active-loans": { - "get": { - "operationId": "api_v1_guest_admin_makerspace_active_loans_list", - "summary": "List active loans awaiting return", + "/api/v1/admin/spaces/{id}/arrival/{booking_id}/pass/": { + "post": { + "operationId": "api_v1_admin_spaces_arrival_pass_create", + "description": "Requires resource check_in, bookings.passes/checkin and current active arrival. Answer-free projection, no override or cached PDF.", "parameters": [ - { - "name": "id", - "in": "query", - "required": false, - "description": "Select one result within the authorized list.", - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 9223372036854775807 - } - }, { "in": "path", - "name": "makerspace_id", + "name": "booking_id", "schema": { "type": "integer" }, "required": true }, { - "name": "ordering", - "required": false, - "in": "query", - "description": "Which field to use when ordering the results.", - "schema": { - "type": "string" - } - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", + "in": "path", + "name": "id", "schema": { "type": "integer" - } - }, - { - "name": "search", - "required": false, - "in": "query", - "description": "A search term (requested-for, requester name/email).", - "schema": { - "type": "string" - } + }, + "required": true } ], "tags": [ - "Admin requests" + "Admin bookings" ], "security": [ { @@ -53641,13 +54545,14 @@ "responses": { "200": { "content": { - "application/json": { + "application/pdf": { "schema": { - "$ref": "#/components/schemas/PaginatedAdminRequestList" + "type": "string", + "format": "binary" } } }, - "description": "" + "description": "Private answer-free booking pass. Never contains a bearer QR." }, "400": { "content": { @@ -53657,7 +54562,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -53667,7 +54572,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -53677,7 +54582,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -53687,16 +54592,63 @@ } } }, - "description": "Not found." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/guest-admin/requests/{id}/return": { + "/api/v1/admin/spaces/{id}/arrival/{booking_id}/reverse/": { "post": { - "operationId": "api_v1_guest_admin_requests_return_create", - "summary": "Return issued request items", + "operationId": "api_v1_admin_spaces_arrival_reverse_create", "parameters": [ + { + "in": "path", + "name": "booking_id", + "schema": { + "type": "integer" + }, + "required": true + }, { "in": "path", "name": "id", @@ -53707,28 +54659,8 @@ } ], "tags": [ - "Admin requests" + "Admin bookings" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReturnRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ReturnRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ReturnRequest" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -53739,7 +54671,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdminRequest" + "$ref": "#/components/schemas/BookingArrival" } } }, @@ -53753,7 +54685,7 @@ } } }, - "description": "Invalid request." + "description": "" }, "401": { "content": { @@ -53763,7 +54695,7 @@ } } }, - "description": "Authentication required." + "description": "" }, "403": { "content": { @@ -53773,7 +54705,7 @@ } } }, - "description": "Permission denied." + "description": "" }, "404": { "content": { @@ -53783,7 +54715,7 @@ } } }, - "description": "Not found." + "description": "" }, "409": { "content": { @@ -53793,9 +54725,9 @@ } } }, - "description": "Workflow conflict." + "description": "" }, - "503": { + "429": { "content": { "application/json": { "schema": { @@ -53803,107 +54735,38 @@ } } }, - "description": "Service unavailable." - } - } - } - }, - "/api/v1/health/": { - "get": { - "operationId": "api_v1_health_retrieve", - "summary": "Health check", - "tags": [ - "Health" - ], - "security": [ - { - "jwtAuth": [] + "description": "" }, - {} - ], - "responses": { - "200": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Health" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, "503": { - "description": "The recovery gate is closed: the database is unreachable or a restore is running." - } - } - } - }, - "/api/v1/health/readiness/": { - "get": { - "operationId": "api_v1_health_readiness_retrieve", - "summary": "Readiness check", - "tags": [ - "Health" - ], - "security": [ - { - "jwtAuth": [] - }, - {} - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Readiness" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "503": { - "description": "Not ready: host marker, database identity, migrations, fences or PII keys failed." } } } }, - "/api/v1/integrations/makerspace/{makerspace_id}/bounce-webhook": { + "/api/v1/admin/spaces/{id}/arrival/confirm/": { "post": { - "operationId": "api_v1_integrations_makerspace_bounce_webhook_create", - "description": "HMAC-SHA256 hex of ASCII timestamp + dot + raw JSON body. Five-minute replay window. Hard bounces/complaints only.", + "operationId": "api_v1_admin_spaces_arrival_confirm_create", "parameters": [ - { - "in": "header", - "name": "X-SpaceWorks-Signature", - "schema": { - "type": "string" - }, - "required": true - }, - { - "in": "header", - "name": "X-SpaceWorks-Timestamp", - "schema": { - "type": "string" - }, - "required": true - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -53911,115 +54774,163 @@ } ], "tags": [ - "Notifications" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Bounce" + "$ref": "#/components/schemas/BookingCredential" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/Bounce" + "$ref": "#/components/schemas/BookingCredential" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/Bounce" + "$ref": "#/components/schemas/BookingCredential" } } }, "required": true }, + "security": [ + { + "jwtAuth": [] + } + ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BounceResult" + "$ref": "#/components/schemas/BookingArrival" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "423": { - "description": "Makerspace writes are frozen for tenant migration." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "503": { - "description": "A required dependency is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/integrations/makerspace/{makerspace_id}/delivery-logs": { - "get": { - "operationId": "api_v1_integrations_makerspace_delivery_logs_retrieve", + "/api/v1/admin/spaces/{id}/arrival/resolve/": { + "post": { + "operationId": "api_v1_admin_spaces_arrival_resolve_create", "parameters": [ - { - "in": "query", - "name": "channel", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "destination", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "event", - "schema": { - "type": "string" - } - }, - { - "in": "query", - "name": "feature", - "schema": { - "type": "string" - } - }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string" - } } ], "tags": [ - "Notification delivery" + "Admin bookings" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingCredential" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BookingCredential" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BookingCredential" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -54030,30 +54941,99 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeliveryPage" + "$ref": "#/components/schemas/BookingArrival" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/integrations/makerspace/{makerspace_id}/delivery-logs/{kind}/{id}/retry": { - "post": { - "operationId": "api_v1_integrations_makerspace_delivery_logs_retry_create", + "/api/v1/admin/spaces/{id}/booking-rules/": { + "get": { + "operationId": "api_v1_admin_spaces_booking_rules_retrieve", + "summary": "Retrieve booking rules for a space", "parameters": [ { "in": "path", @@ -54062,47 +55042,11 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "kind", - "schema": { - "type": "string" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Notification delivery" + "Admin bookings" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Retry" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/Retry" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/Retry" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] @@ -54113,40 +55057,52 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Delivery" + "$ref": "#/components/schemas/BookableSpaceBookingRules" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid booking-rule values or bookings module disabled." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." - }, - "503": { - "description": "A required dependency is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." } } - } - }, - "/api/v1/integrations/makerspace/{makerspace_id}/delivery-test": { - "post": { - "operationId": "api_v1_integrations_makerspace_delivery_test_create", + }, + "patch": { + "operationId": "api_v1_admin_spaces_booking_rules_partial_update", + "summary": "Update booking rules for a space", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -54154,23 +55110,23 @@ } ], "tags": [ - "Notification delivery" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeliveryTest" + "$ref": "#/components/schemas/PatchedBookableSpaceBookingRules" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/DeliveryTest" + "$ref": "#/components/schemas/PatchedBookableSpaceBookingRules" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/DeliveryTest" + "$ref": "#/components/schemas/PatchedBookableSpaceBookingRules" } } } @@ -54185,48 +55141,131 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeliveryTestResult" + "$ref": "#/components/schemas/BookableSpaceBookingRules" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid booking-rule values or bookings module disabled." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." }, - "503": { - "description": "A required dependency is unavailable." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." } } } }, - "/api/v1/integrations/makerspace/{makerspace_id}/email-suppressions": { + "/api/v1/admin/spaces/{id}/bookings/": { "get": { - "operationId": "api_v1_integrations_makerspace_email_suppressions_retrieve", + "operationId": "api_v1_admin_spaces_bookings_retrieve", + "summary": "List bookings for a space", "parameters": [ + { + "in": "query", + "name": "booking_id", + "schema": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 1, + "format": "int64" + } + }, + { + "in": "query", + "name": "ends_at", + "schema": { + "type": "string", + "format": "date-time" + } + }, { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "ordering", + "schema": { + "type": "string" + }, + "description": "Allowlisted sort field; prefix with '-' for descending." + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number, 24 rows per page." + }, + { + "in": "query", + "name": "starts_at", + "schema": { + "type": "string", + "format": "date-time" + } + }, + { + "in": "query", + "name": "status", + "schema": { + "enum": [ + "pending", + "confirmed", + "rejected", + "cancelled", + "completed", + "no_show" + ], + "type": "string", + "minLength": 1 + }, + "description": "* `pending` - Pending\n* `confirmed` - Confirmed\n* `rejected` - Rejected\n* `cancelled` - Cancelled\n* `completed` - Completed\n* `no_show` - No-show" } ], "tags": [ - "Notification delivery" + "Admin bookings" ], "security": [ { @@ -54238,29 +55277,61 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuppressionPage" + "$ref": "#/components/schemas/BookingListResponse" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." + }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." } } }, "post": { - "operationId": "api_v1_integrations_makerspace_email_suppressions_create", + "operationId": "api_v1_admin_spaces_bookings_create", + "summary": "Book a space on behalf of an active member", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -54268,23 +55339,23 @@ } ], "tags": [ - "Notification delivery" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuppressionWrite" + "$ref": "#/components/schemas/StaffBookingInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/SuppressionWrite" + "$ref": "#/components/schemas/StaffBookingInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/SuppressionWrite" + "$ref": "#/components/schemas/StaffBookingInput" } } }, @@ -54300,36 +55371,90 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Suppression" + "$ref": "#/components/schemas/BookingAdmin" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid input or bookings module disabled." }, "401": { - "description": "Not signed in." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant frozen for migration." }, "503": { - "description": "A required dependency is unavailable." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant write gate unavailable." } } } }, - "/api/v1/integrations/makerspace/{makerspace_id}/email-suppressions/{id}": { - "delete": { - "operationId": "api_v1_integrations_makerspace_email_suppressions_destroy", + "/api/v1/admin/spaces/{id}/broadcasts/": { + "get": { + "operationId": "api_v1_admin_spaces_broadcasts_retrieve", + "description": "Requires live resource manage_reservations authority, bookings.broadcasts, notifications and email. Verified account email only; membership consent, preferences, requester toggle, matrix and quiet hours apply.", "parameters": [ { "in": "path", @@ -54340,59 +55465,15 @@ "required": true }, { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Notification delivery" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "Suppression removed after operator review." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." - }, - "503": { - "description": "A required dependency is unavailable." - } - } - } - }, - "/api/v1/integrations/makerspace/{makerspace_id}/timing-policy": { - "get": { - "operationId": "api_v1_integrations_makerspace_timing_policy_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "page", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Notifications" + "Admin bookings" ], "security": [ { @@ -54404,196 +55485,53 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TimingPolicy" + "$ref": "#/components/schemas/BookingBroadcastList" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - }, - "patch": { - "operationId": "api_v1_integrations_makerspace_timing_policy_partial_update", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Notifications" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedTimingPolicy" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedTimingPolicy" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PatchedTimingPolicy" - } - } - } - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TimingPolicy" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." - }, - "503": { - "description": "A required dependency is unavailable." - } - } - } - }, - "/api/v1/integrations/push/devices": { - "post": { - "operationId": "api_v1_integrations_push_devices_create", - "tags": [ - "Native push" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PushDeviceRegistration" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PushDeviceRegistration" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PushDeviceRegistration" - } - } + "description": "" }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PushDevice" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "201": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PushDevice" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, "409": { - "description": "Conflicts with the current state." - }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - }, - "503": { - "description": "A required dependency is unavailable." - } - } - } - }, - "/api/v1/integrations/push/devices/{device_id}": { - "delete": { - "operationId": "api_v1_integrations_push_devices_destroy", - "parameters": [ - { - "in": "path", - "name": "device_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Native push" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" - }, - "401": { "content": { "application/json": { "schema": { @@ -54603,7 +55541,7 @@ }, "description": "" }, - "403": { + "423": { "content": { "application/json": { "schema": { @@ -54613,7 +55551,7 @@ }, "description": "" }, - "404": { + "429": { "content": { "application/json": { "schema": { @@ -54622,206 +55560,50 @@ } }, "description": "" - } - } - } - }, - "/api/v1/integrations/telegram/test-alert": { - "post": { - "operationId": "api_v1_integrations_telegram_test_alert_create", - "summary": "Send Telegram test alert", - "tags": [ - "Telegram" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TelegramTestAlert" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/TelegramTestAlert" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/TelegramTestAlert" - } - } - }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "description": "Delivery attempt result; `delivered: false` with a reason when Telegram is unconfigured or rejects the message." - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - } - } - } - }, - "/api/v1/integrations/telegram/webhook": { - "post": { - "operationId": "api_v1_integrations_telegram_webhook_create", - "description": "Retained so an already-registered webhook does not retry forever. Callback queries are acknowledged and discarded: accepting and rejecting borrow requests happens in the staff console, never from chat.", - "summary": "Acknowledge a Telegram webhook (no action is taken)", - "tags": [ - "Telegram" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TelegramWebhook" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/TelegramWebhook" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/TelegramWebhook" - } - } - } - }, - "responses": { - "200": { - "description": "Acknowledged; no action taken." - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "403": { - "description": "TELEGRAM_WEBHOOK_SECRET is unset, or the secret-token header is wrong." }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - } - } - } - }, - "/api/v1/internal/cron/return-reminders": { - "post": { - "operationId": "api_v1_internal_cron_return_reminders_create", - "tags": [ - "Health" - ], - "responses": { - "200": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReturnReminderCronResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Return reminder run result." - }, - "403": { - "description": "Invalid cron secret." - }, - "404": { - "description": "Cron endpoint is not configured." - } - } - } - }, - "/api/v1/internal/tls-check": { - "get": { - "operationId": "api_v1_internal_tls_check_retrieve", - "summary": "Check whether on-demand TLS may be issued for a domain", - "parameters": [ - { - "in": "query", - "name": "domain", - "schema": { - "type": "string" - }, - "description": "Canonical hostname requested for on-demand TLS issuance.", - "required": true - } - ], - "tags": [ - "Internal" - ], - "responses": { - "200": { - "description": "TLS issuance is allowed." - }, - "403": { - "description": "TLS issuance is denied." - }, - "400": { - "description": "Managed mode: the Host header is not an infra host." - }, - "503": { - "description": "The recovery gate is closed; deny issuance." + "description": "" } } - } - }, - "/api/v1/kiosk-stations/{public_token}/commands/online/": { + }, "post": { - "operationId": "api_v1_kiosk_stations_commands_online_create", - "summary": "Run one online kiosk command through the workflow", + "operationId": "api_v1_admin_spaces_broadcasts_create", + "description": "Requires live resource manage_reservations authority, bookings.broadcasts, notifications and email. Verified account email only; membership consent, preferences, requester toggle, matrix and quiet hours apply. Compose a draft for a future start window of at most 366 days.", "parameters": [ - { - "in": "header", - "name": "X-Kiosk-Operator-Lease", - "schema": { - "type": "string" - }, - "description": "Signed operator lease returned by the operator-lease route.", - "required": true - }, { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Kiosk stations" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskOnlineCommand" + "$ref": "#/components/schemas/BookingBroadcastInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/KioskOnlineCommand" + "$ref": "#/components/schemas/BookingBroadcastInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/KioskOnlineCommand" + "$ref": "#/components/schemas/BookingBroadcastInput" } } }, @@ -54833,11 +55615,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskOnlineCommandResult" + "$ref": "#/components/schemas/BookingBroadcast" } } }, @@ -54851,7 +55633,7 @@ } } }, - "description": "Invalid payload or capability disabled." + "description": "" }, "401": { "content": { @@ -54861,7 +55643,7 @@ } } }, - "description": "Operator is not signed in." + "description": "" }, "403": { "content": { @@ -54871,7 +55653,7 @@ } } }, - "description": "Invalid Station session, lease or authority." + "description": "" }, "404": { "content": { @@ -54881,7 +55663,27 @@ } } }, - "description": "Target not found in this makerspace." + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "429": { "content": { @@ -54891,7 +55693,7 @@ } } }, - "description": "Request rate limit exceeded." + "description": "" }, "503": { "content": { @@ -54901,57 +55703,45 @@ } } }, - "description": "Storage unavailable." + "description": "" } } } }, - "/api/v1/kiosk-stations/{public_token}/evidence/prepare/": { + "/api/v1/admin/spaces/{id}/broadcasts/{campaign_id}/cancel/": { "post": { - "operationId": "api_v1_kiosk_stations_evidence_prepare_create", - "summary": "Presign a direct-to-staging evidence upload", + "operationId": "api_v1_admin_spaces_broadcasts_cancel_create", + "description": "Requires live resource manage_reservations authority, bookings.broadcasts, notifications and email. Verified account email only; membership consent, preferences, requester toggle, matrix and quiet hours apply. Queue limit: ten per hour per tenant; cancel stops unclaimed sends.", "parameters": [ { - "in": "header", - "name": "X-Kiosk-Operator-Lease", + "in": "path", + "name": "campaign_id", "schema": { - "type": "string" + "type": "integer" }, - "description": "Signed operator lease returned by the operator-lease route.", "required": true }, { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Kiosk stations" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskEvidencePrepare" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/KioskEvidencePrepare" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/KioskEvidencePrepare" + "type": "object", + "additionalProperties": false } } - }, - "required": true + } }, "security": [ { @@ -54959,11 +55749,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EvidenceUrlResponse" + "$ref": "#/components/schemas/BookingBroadcast" } } }, @@ -54977,7 +55767,7 @@ } } }, - "description": "Invalid payload or capability disabled." + "description": "" }, "401": { "content": { @@ -54987,7 +55777,7 @@ } } }, - "description": "Operator is not signed in." + "description": "" }, "403": { "content": { @@ -54997,7 +55787,7 @@ } } }, - "description": "Invalid Station session, lease or authority." + "description": "" }, "404": { "content": { @@ -55007,9 +55797,9 @@ } } }, - "description": "Target not found in this makerspace." + "description": "" }, - "429": { + "409": { "content": { "application/json": { "schema": { @@ -55017,9 +55807,9 @@ } } }, - "description": "Request rate limit exceeded." + "description": "" }, - "503": { + "423": { "content": { "application/json": { "schema": { @@ -55027,46 +55817,83 @@ } } }, - "description": "Storage unavailable." + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/kiosk-stations/{public_token}/operator-lease/": { + "/api/v1/admin/spaces/{id}/broadcasts/{campaign_id}/queue/": { "post": { - "operationId": "api_v1_kiosk_stations_operator_lease_create", - "summary": "Issue a human operator lease on this Station session", + "operationId": "api_v1_admin_spaces_broadcasts_queue_create", + "description": "Requires live resource manage_reservations authority, bookings.broadcasts, notifications and email. Verified account email only; membership consent, preferences, requester toggle, matrix and quiet hours apply. Queue limit: ten per hour per tenant; cancel stops unclaimed sends.", "parameters": [ { "in": "path", - "name": "public_token", + "name": "campaign_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" }, "required": true } ], "tags": [ - "Kiosk stations" + "Admin bookings" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false + } + } + } + }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskOperatorLease" + "$ref": "#/components/schemas/BookingBroadcast" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -55074,9 +55901,9 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -55084,7 +55911,7 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "" }, "403": { "content": { @@ -55094,9 +55921,9 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "" }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -55104,46 +55931,29 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." - } - } - }, - "delete": { - "operationId": "api_v1_kiosk_stations_operator_lease_destroy", - "summary": "Revoke the caller's operator lease (operator sign-out)", - "parameters": [ - { - "in": "header", - "name": "X-Kiosk-Operator-Lease", - "schema": { - "type": "string" + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "description": "Signed operator lease returned by the operator-lease route.", - "required": true + "description": "" }, - { - "in": "path", - "name": "public_token", - "schema": { - "type": "string", - "format": "uuid" + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Kiosk stations" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" + "description": "" }, - "401": { + "429": { "content": { "application/json": { "schema": { @@ -55151,9 +55961,9 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "" }, - "403": { + "503": { "content": { "application/json": { "schema": { @@ -55161,78 +55971,59 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "" } } } }, - "/api/v1/kiosk-stations/{public_token}/replay/{client_command_id}/execute/": { + "/api/v1/admin/spaces/{id}/clone/": { "post": { - "operationId": "api_v1_kiosk_stations_replay_execute_create", - "description": "Same Station cookie, origin/CSRF and human lease as prepare. Current authority and domain rules are checked again; duplicate success returns stable references.", - "summary": "Execute or recover one prepared handover command", + "operationId": "api_v1_admin_spaces_clone_create", + "description": "Requires tenant MANAGE_BOOKINGS and bookings.clone. Copying protected rules also requires MANAGE_MAKERSPACE.", + "summary": "Clone a resource into a private copy", "parameters": [ - { - "in": "header", - "name": "X-Kiosk-Operator-Lease", - "schema": { - "type": "string" - }, - "description": "Signed operator lease returned by the operator-lease route.", - "required": true - }, - { - "in": "header", - "name": "X-Station-CSRF", - "schema": { - "type": "string" - }, - "required": true - }, - { - "in": "path", - "name": "client_command_id", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - }, { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Kiosk stations" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReplayEnvelope" + "$ref": "#/components/schemas/BookingCloneInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BookingCloneInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BookingCloneInput" } } - }, - "required": true + } }, "security": [ { - "KioskStationCookie": [] - }, - {} + "jwtAuth": [] + } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReplayResult" + "$ref": "#/components/schemas/BookableSpaceAdmin" } } }, @@ -55246,7 +56037,17 @@ } } }, - "description": "Invalid command, target, module or feature disabled." + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." }, "403": { "content": { @@ -55256,9 +56057,9 @@ } } }, - "description": "Station, origin, session, operator lease or live permission refused." + "description": "Permission denied." }, - "409": { + "404": { "content": { "application/json": { "schema": { @@ -55266,9 +56067,9 @@ } } }, - "description": "Ownership conflict, expired command, predecessor refusal or concurrent claim." + "description": "Not found." }, - "423": { + "409": { "content": { "application/json": { "schema": { @@ -55276,7 +56077,7 @@ } } }, - "description": "Tenant migration source gate is closed." + "description": "Workflow conflict." }, "429": { "content": { @@ -55286,7 +56087,7 @@ } } }, - "description": "Replay rate limit exceeded." + "description": "Too many requests." }, "503": { "content": { @@ -55296,69 +56097,39 @@ } } }, - "description": "Storage or replay temporarily unavailable." + "description": "Service unavailable." } } } }, - "/api/v1/kiosk-stations/{public_token}/replay/prepare/": { + "/api/v1/admin/spaces/{id}/deactivate/": { "post": { - "operationId": "api_v1_kiosk_stations_replay_prepare_create", - "description": "Requires the path-bound Station cookie, exact Origin/Referer, X-Station-CSRF and signed human lease; no browser login required. Send the identical envelope to execute.", - "summary": "Prepare an ordered command and staging evidence upload", + "operationId": "api_v1_admin_spaces_deactivate_create", + "summary": "Deactivate a bookable space", "parameters": [ - { - "in": "header", - "name": "X-Kiosk-Operator-Lease", - "schema": { - "type": "string" - }, - "description": "Signed operator lease returned by the operator-lease route.", - "required": true - }, - { - "in": "header", - "name": "X-Station-CSRF", - "schema": { - "type": "string" - }, - "required": true - }, { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Kiosk stations" + "Admin bookings" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ReplayEnvelope" - } - } - }, - "required": true - }, "security": [ { - "KioskStationCookie": [] - }, - {} + "jwtAuth": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReplayResult" + "$ref": "#/components/schemas/BookableSpaceAdmin" } } }, @@ -55372,19 +56143,9 @@ } } }, - "description": "Invalid command, target, module or feature disabled." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Station, origin, session, operator lease or live permission refused." + "description": "Invalid request." }, - "409": { + "401": { "content": { "application/json": { "schema": { @@ -55392,9 +56153,9 @@ } } }, - "description": "Ownership conflict, expired command, predecessor refusal or concurrent claim." + "description": "Authentication required." }, - "423": { + "403": { "content": { "application/json": { "schema": { @@ -55402,9 +56163,9 @@ } } }, - "description": "Tenant migration source gate is closed." + "description": "Permission denied." }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -55412,9 +56173,9 @@ } } }, - "description": "Replay rate limit exceeded." + "description": "Not found." }, - "503": { + "409": { "content": { "application/json": { "schema": { @@ -55422,61 +56183,63 @@ } } }, - "description": "Storage or replay temporarily unavailable." + "description": "Workflow conflict." } } } }, - "/api/v1/kiosk-stations/{public_token}/session/": { - "post": { - "operationId": "api_v1_kiosk_stations_session_create", - "summary": "Exchange a kiosk Station PIN for a Station session", + "/api/v1/admin/spaces/{id}/eligible-members/": { + "get": { + "operationId": "api_v1_admin_spaces_eligible_members_retrieve", + "summary": "List active members for staff booking", "parameters": [ { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } } ], "tags": [ - "Kiosk stations" + "Admin bookings" + ], + "security": [ + { + "jwtAuth": [] + } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/KioskStationPin" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/KioskStationPin" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/KioskStationPin" - } - } - }, - "required": true - }, "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskStationBootstrap" + "$ref": "#/components/schemas/BookingEligibleMemberList" } } }, "description": "" }, "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid input or bookings module disabled." + }, + "401": { "content": { "application/json": { "schema": { @@ -55484,7 +56247,7 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "Authentication required." }, "403": { "content": { @@ -55494,9 +56257,9 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "Permission denied." }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -55504,36 +56267,57 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "Not found." } } - }, + } + }, + "/api/v1/admin/spaces/{id}/image/": { "delete": { - "operationId": "api_v1_kiosk_stations_session_destroy", - "summary": "End the Station session and revoke its operator leases", + "operationId": "api_v1_admin_spaces_image_destroy", + "summary": "Delete a space image", "parameters": [ { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Kiosk stations" + "Admin bookings" ], "security": [ { - "KioskStationCookie": [] + "jwtAuth": [] } ], "responses": { "204": { "description": "No response body" }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, "403": { "content": { "application/json": { @@ -55542,9 +56326,9 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "Permission denied." }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -55552,53 +56336,53 @@ } } }, - "description": "Invalid Station credential, session, origin, feature or lease." + "description": "Not found." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." } } } }, - "/api/v1/kiosk-stations/{public_token}/work-package/": { + "/api/v1/admin/spaces/{id}/image/finalize/": { "post": { - "operationId": "api_v1_kiosk_stations_work_package_create", - "summary": "Load the minimal work package for one handover", + "operationId": "api_v1_admin_spaces_image_finalize_create", + "summary": "Finalize and attach a space image", "parameters": [ - { - "in": "header", - "name": "X-Kiosk-Operator-Lease", - "schema": { - "type": "string" - }, - "description": "Signed operator lease returned by the operator-lease route.", - "required": true - }, { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Kiosk stations" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskWorkPackageRequest" + "$ref": "#/components/schemas/SpaceImageFinalizeRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/KioskWorkPackageRequest" + "$ref": "#/components/schemas/SpaceImageFinalizeRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/KioskWorkPackageRequest" + "$ref": "#/components/schemas/SpaceImageFinalizeRequest" } } }, @@ -55614,7 +56398,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KioskWorkPackage" + "$ref": "#/components/schemas/BookableSpaceAdmin" } } }, @@ -55628,7 +56412,7 @@ } } }, - "description": "Invalid payload or capability disabled." + "description": "Invalid request." }, "401": { "content": { @@ -55638,7 +56422,7 @@ } } }, - "description": "Operator is not signed in." + "description": "Authentication required." }, "403": { "content": { @@ -55648,7 +56432,7 @@ } } }, - "description": "Invalid Station session, lease or authority." + "description": "Permission denied." }, "404": { "content": { @@ -55658,9 +56442,9 @@ } } }, - "description": "Target not found in this makerspace." + "description": "Not found." }, - "429": { + "503": { "content": { "application/json": { "schema": { @@ -55668,75 +56452,85 @@ } } }, - "description": "Request rate limit exceeded." + "description": "Service unavailable." } } } }, - "/api/v1/live/": { - "get": { - "operationId": "api_v1_live_retrieve", - "description": "text/event-stream of `change` events `{kind, makerspace_id, target_type, target_id, actor_id, ts}` for every makerspace the session may see. Clients invalidate cached queries on an event and refetch; no tenant content travels on the stream. 503 when the deployment has no Redis.", - "summary": "Live update stream (Server-Sent Events)", + "/api/v1/admin/spaces/{id}/image/presign/": { + "post": { + "operationId": "api_v1_admin_spaces_image_presign_create", + "summary": "Create a space image upload URL", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], "tags": [ - "Health" + "Admin bookings" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SpaceImagePresignRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/SpaceImagePresignRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/SpaceImagePresignRequest" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { - "text/event-stream": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/SpaceImagePresignResponse" } } }, "description": "" }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Account access is restricted." + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." }, - "503": { - "description": "No usable Redis (unset, misconfigured or unreachable); poll instead." - } - } - } - }, - "/api/v1/member/archived-payments": { - "get": { - "operationId": "api_v1_member_archived_payments_list", - "description": "Lists archived makerspaces where the caller retains an active membership and has payment history available to read or settle.", - "summary": "Discover the caller's archived makerspace payments", - "tags": [ - "Payments" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "401": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ArchivedPaymentSummary" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Authentication required." }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -55744,18 +56538,39 @@ } } }, - "description": "" + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/account/cancel": { + "/api/v1/admin/spaces/{id}/pass-preview/": { "post": { - "operationId": "api_v1_member_makerspaces_account_cancel_create", + "operationId": "api_v1_admin_spaces_pass_preview_create", + "description": "Requires edit_details. Fixed sample recipient only; no booking answers, notes, contacts or QR.", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -55763,27 +56578,26 @@ } ], "tags": [ - "Member account" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipReason" + "$ref": "#/components/schemas/BookingPassTemplate" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MembershipReason" + "$ref": "#/components/schemas/BookingPassTemplate" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MembershipReason" + "$ref": "#/components/schemas/BookingPassTemplate" } } - }, - "required": true + } }, "security": [ { @@ -55793,42 +56607,30 @@ "responses": { "200": { "content": { - "application/json": { + "application/pdf": { "schema": { - "$ref": "#/components/schemas/MembershipAccount" + "type": "string", + "format": "binary" } } }, - "description": "" + "description": "Private answer-free booking pass. Never contains a bearer QR." }, "400": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55838,7 +56640,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55848,17 +56650,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "423": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55868,7 +56670,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55878,7 +56690,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55887,14 +56699,14 @@ } } }, - "/api/v1/member/makerspaces/{makerspace_id}/account/exports": { + "/api/v1/admin/spaces/{id}/pass-template/": { "get": { - "operationId": "api_v1_member_makerspaces_account_exports_retrieve", - "summary": "List the caller's personal exports", + "operationId": "api_v1_admin_spaces_pass_template_retrieve", + "description": "Requires live resource edit_details and bookings.passes. Answer-free fields only.", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -55902,7 +56714,7 @@ } ], "tags": [ - "Member account" + "Admin bookings" ], "security": [ { @@ -55914,7 +56726,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubjectExportList" + "$ref": "#/components/schemas/BookingPassTemplate" } } }, @@ -55924,30 +56736,17 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55957,7 +56756,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55967,17 +56766,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "423": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55987,7 +56786,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -55997,7 +56806,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56005,13 +56814,12 @@ } } }, - "post": { - "operationId": "api_v1_member_makerspaces_account_exports_create", - "summary": "Request a personal ZIP/JSON data export", + "put": { + "operationId": "api_v1_admin_spaces_pass_template_update", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -56019,14 +56827,23 @@ } ], "tags": [ - "Member account" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": false + "$ref": "#/components/schemas/BookingPassTemplate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BookingPassTemplate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BookingPassTemplate" } } } @@ -56037,11 +56854,11 @@ } ], "responses": { - "202": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubjectExport" + "$ref": "#/components/schemas/BookingPassTemplate" } } }, @@ -56051,30 +56868,17 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "" }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56084,7 +56888,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56094,17 +56898,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "423": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56114,7 +56918,17 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56124,7 +56938,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56133,31 +56947,106 @@ } } }, - "/api/v1/member/makerspaces/{makerspace_id}/account/exports/{job_id}": { - "get": { - "operationId": "api_v1_member_makerspaces_account_exports_retrieve_2", - "summary": "Poll the caller's personal export", + "/api/v1/admin/spaces/{id}/recurring-bookings/": { + "post": { + "operationId": "api_v1_admin_spaces_recurring_bookings_create", + "description": "Create a finite weekly series in makerspace local time. Each occurrence has its own flat charge and cancellation. Conflicts are reported, never silently skipped.", "parameters": [ { "in": "path", - "name": "job_id", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true + } + ], + "tags": [ + "Admin bookings" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RecurringBookingInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/RecurringBookingInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RecurringBookingInput" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RecurrenceResult" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, + "409": { + "description": "Conflicts with the current state." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/admin/spaces/{id}/staff-grants/": { + "get": { + "operationId": "api_v1_admin_spaces_staff_grants_retrieve", + "description": "Requires live tenant MANAGE_BOOKINGS and bookings.staff_grants. Resource grantees cannot delegate. Invitations expire after seven days; sender authority and permission subset are rechecked on redemption.", + "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } } ], "tags": [ - "Member account" + "Admin bookings" ], "security": [ { @@ -56169,60 +57058,47 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubjectExport" + "$ref": "#/components/schemas/BookingStaffGrantList" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56232,7 +57108,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56242,7 +57118,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56252,40 +57128,49 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Field validation, module or feature disabled." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/account/exports/{job_id}/download": { - "post": { - "operationId": "api_v1_member_makerspaces_account_exports_download_create", - "summary": "Sign a short-lived personal export download URL", + "/api/v1/admin/spaces/{id}/staff-invites/": { + "get": { + "operationId": "api_v1_admin_spaces_staff_invites_retrieve", + "description": "Requires live tenant MANAGE_BOOKINGS and bookings.staff_grants. Resource grantees cannot delegate. Invitations expire after seven days; sender authority and permission subset are rechecked on redemption.", "parameters": [ { "in": "path", - "name": "job_id", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true }, { - "in": "path", - "name": "makerspace_id", + "in": "query", + "name": "page", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Member account" + "Admin bookings" ], "security": [ { @@ -56297,105 +57182,102 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DataExportDownloadUrl" + "$ref": "#/components/schemas/BookingStaffGrantInviteList" } } }, "description": "" }, - "503": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "403": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "403": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "404": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "429": { + "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Field validation, module or feature disabled." } } - } - }, - "/api/v1/member/makerspaces/{makerspace_id}/account/leave": { + }, "post": { - "operationId": "api_v1_member_makerspaces_account_leave_create", + "operationId": "api_v1_admin_spaces_staff_invites_create", + "description": "Requires live tenant MANAGE_BOOKINGS and bookings.staff_grants. Resource grantees cannot delegate. Invitations expire after seven days; sender authority and permission subset are rechecked on redemption.", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -56403,23 +57285,23 @@ } ], "tags": [ - "Member account" + "Admin bookings" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipReason" + "$ref": "#/components/schemas/BookingStaffGrantInviteInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MembershipReason" + "$ref": "#/components/schemas/BookingStaffGrantInviteInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MembershipReason" + "$ref": "#/components/schemas/BookingStaffGrantInviteInput" } } }, @@ -56431,64 +57313,51 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipAccount" + "$ref": "#/components/schemas/BookingStaffGrantInviteCreated" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "" }, - "401": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "403": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "404": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56498,7 +57367,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56508,7 +57377,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, @@ -56518,22 +57387,33 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Field validation, module or feature disabled." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/account/membership": { + "/api/v1/admin/spaces/{id}/website/": { "get": { - "operationId": "api_v1_member_makerspaces_account_membership_retrieve", + "operationId": "api_v1_admin_spaces_website_retrieve", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -56541,7 +57421,7 @@ } ], "tags": [ - "Member account" + "Booking website" ], "security": [ { @@ -56553,7 +57433,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipAccount" + "$ref": "#/components/schemas/BookingWebsite" } } }, @@ -56563,95 +57443,80 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "Invalid content, stale revision or disabled feature." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Sign in required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Missing resource permission or wrong origin." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Unknown or out-of-scope resource/image." }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Rate limit exceeded." }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Tenant frozen." }, "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Storage or write gate unavailable." } } - } - }, - "/api/v1/member/makerspaces/{makerspace_id}/account/profile": { - "get": { - "operationId": "api_v1_member_makerspaces_account_profile_retrieve", + }, + "put": { + "operationId": "api_v1_admin_spaces_website_update", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -56659,8 +57524,28 @@ } ], "tags": [ - "Member account" + "Booking website" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingWebsiteContentInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BookingWebsiteContentInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BookingWebsiteContentInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -56671,7 +57556,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountProfile" + "$ref": "#/components/schemas/BookingWebsite" } } }, @@ -56681,93 +57566,82 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "Invalid content, stale revision or disabled feature." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Sign in required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Missing resource permission or wrong origin." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Unknown or out-of-scope resource/image." }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Rate limit exceeded." }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Tenant frozen." }, "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Storage or write gate unavailable." } } - }, - "patch": { - "operationId": "api_v1_member_makerspaces_account_profile_partial_update", + } + }, + "/api/v1/admin/spaces/{id}/website/assets/": { + "post": { + "operationId": "api_v1_admin_spaces_website_assets_create", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -56775,26 +57649,17 @@ } ], "tags": [ - "Member account" + "Booking website" ], "requestBody": { "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PatchedAccountProfile" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PatchedAccountProfile" - } - }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedAccountProfile" + "$ref": "#/components/schemas/BookingWebsiteAssetInput" } } - } + }, + "required": true }, "security": [ { @@ -56802,11 +57667,11 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountProfile" + "$ref": "#/components/schemas/BookingWebsiteAsset" } } }, @@ -56816,95 +57681,90 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "Invalid content, stale revision or disabled feature." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Sign in required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Missing resource permission or wrong origin." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Unknown or out-of-scope resource/image." }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Rate limit exceeded." }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Tenant frozen." }, "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Storage or write gate unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/account/sessions": { + "/api/v1/admin/spaces/{id}/website/assets/{asset_id}/": { "get": { - "operationId": "api_v1_member_makerspaces_account_sessions_retrieve", + "operationId": "api_v1_admin_spaces_website_assets_retrieve", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "asset_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "id", "schema": { "type": "integer" }, @@ -56912,7 +57772,7 @@ } ], "tags": [ - "Member account" + "Booking website" ], "security": [ { @@ -56922,107 +57782,93 @@ "responses": { "200": { "content": { - "application/json": { + "image/webp": { "schema": { - "$ref": "#/components/schemas/AccountSessionPage" + "type": "string", + "format": "binary" } } }, - "description": "" + "description": "Private no-store WebP preview." }, "400": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "Invalid content, stale revision or disabled feature." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Sign in required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Missing resource permission or wrong origin." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Unknown or out-of-scope resource/image." }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Rate limit exceeded." }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Tenant frozen." }, "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Storage or write gate unavailable." } } - } - }, - "/api/v1/member/makerspaces/{makerspace_id}/account/sessions/{session_id}": { + }, "delete": { - "operationId": "api_v1_member_makerspaces_account_sessions_destroy", + "operationId": "api_v1_admin_spaces_website_assets_destroy", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "asset_id", "schema": { "type": "integer" }, @@ -57030,15 +57876,15 @@ }, { "in": "path", - "name": "session_id", + "name": "id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "Member account" + "Booking website" ], "security": [ { @@ -57053,96 +57899,82 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Disabled module/feature, invalid input, or unavailable export." + "description": "Invalid content, stale revision or disabled feature." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Sign in required." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Missing resource permission or wrong origin." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Unknown or out-of-scope resource/image." }, - "423": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Rate limit exceeded." }, - "429": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Tenant frozen." }, "503": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AccountFailure" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Storage or write gate unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/activity": { - "get": { - "operationId": "api_v1_member_makerspaces_activity_retrieve", - "summary": "Retrieve the caller's makerspace activity", + "/api/v1/admin/spaces/{id}/website/release/": { + "post": { + "operationId": "api_v1_admin_spaces_website_release_create", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -57150,76 +57982,28 @@ } ], "tags": [ - "Member activity" + "Booking website" ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MemberActivity" - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingWebsiteReleaseInput" } }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BookingWebsiteReleaseInput" } }, - "description": "Membership module disabled." - }, - "401": { - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BookingWebsiteReleaseInput" } - }, - "description": "An active membership is required." + } }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Makerspace not found." - } - } - } - }, - "/api/v1/member/makerspaces/{makerspace_id}/admission": { - "get": { - "operationId": "api_v1_member_makerspaces_admission_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Memberships" - ], + "required": true + }, "security": [ { "jwtAuth": [] @@ -57230,7 +58014,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdmissionRequest" + "$ref": "#/components/schemas/BookingWebsite" } } }, @@ -57244,7 +58028,7 @@ } } }, - "description": "" + "description": "Invalid content, stale revision or disabled feature." }, "401": { "content": { @@ -57254,7 +58038,7 @@ } } }, - "description": "" + "description": "Sign in required." }, "403": { "content": { @@ -57264,7 +58048,7 @@ } } }, - "description": "" + "description": "Missing resource permission or wrong origin." }, "404": { "content": { @@ -57274,9 +58058,9 @@ } } }, - "description": "" + "description": "Unknown or out-of-scope resource/image." }, - "423": { + "429": { "content": { "application/json": { "schema": { @@ -57284,9 +58068,9 @@ } } }, - "description": "" + "description": "Rate limit exceeded." }, - "429": { + "423": { "content": { "application/json": { "schema": { @@ -57294,7 +58078,7 @@ } } }, - "description": "" + "description": "Tenant frozen." }, "503": { "content": { @@ -57304,26 +58088,19 @@ } } }, - "description": "" + "description": "Storage or write gate unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/admission/{request_id}/cancel": { - "post": { - "operationId": "api_v1_member_makerspaces_admission_cancel_create", + "/api/v1/admin/stock-transfers/{id}": { + "get": { + "operationId": "api_v1_admin_stock_transfers_retrieve", + "summary": "Retrieve stock transfer", "parameters": [ { "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "request_id", + "name": "id", "schema": { "type": "integer" }, @@ -57331,7 +58108,7 @@ } ], "tags": [ - "Memberships" + "Stock transfers" ], "security": [ { @@ -57343,7 +58120,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdmissionRequest" + "$ref": "#/components/schemas/StockTransfer" } } }, @@ -57353,90 +58130,87 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "" + "description": "Invalid request." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Permission denied." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + "description": "Not found." + } + } + } + }, + "/api/v1/admin/stocktakes/{id}": { + "get": { + "operationId": "api_v1_admin_stocktakes_retrieve", + "summary": "Retrieve stocktake", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" }, - "description": "" - }, - "423": { + "required": true + } + ], + "tags": [ + "Stocktake" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/Stocktake" } } }, "description": "" }, - "429": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "" + "description": "Invalid request or stocktake state." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/admission/{request_id}/vouch": { + "/api/v1/admin/stocktakes/{id}/apply-adjustments": { "post": { - "operationId": "api_v1_member_makerspaces_admission_vouch_create", + "operationId": "api_v1_admin_stocktakes_apply_adjustments_create", + "summary": "Apply stocktake adjustments", "parameters": [ { "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "request_id", + "name": "id", "schema": { "type": "integer" }, @@ -57444,7 +58218,7 @@ } ], "tags": [ - "Memberships" + "Stocktake" ], "security": [ { @@ -57456,7 +58230,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdmissionVouchOutcome" + "$ref": "#/components/schemas/Stocktake" } } }, @@ -57466,82 +58240,32 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "" + "description": "Invalid request or stocktake state." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Permission denied." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/admission/export": { - "get": { - "operationId": "api_v1_member_makerspaces_admission_export_retrieve", + "/api/v1/admin/stocktakes/{id}/approve": { + "post": { + "operationId": "api_v1_admin_stocktakes_approve_create", + "summary": "Approve stocktake", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -57549,7 +58273,7 @@ } ], "tags": [ - "Memberships" + "Stocktake" ], "security": [ { @@ -57561,7 +58285,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdmissionExport" + "$ref": "#/components/schemas/Stocktake" } } }, @@ -57571,99 +58295,40 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "" + "description": "Invalid request or stocktake state." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Permission denied." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/certifications": { - "get": { - "operationId": "api_v1_member_makerspaces_certifications_retrieve", - "summary": "List your own certification history", + "/api/v1/admin/stocktakes/{id}/complete": { + "post": { + "operationId": "api_v1_admin_stocktakes_complete_create", + "summary": "Complete stocktake", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - }, - "description": "Page number (24 rows per page)." } ], "tags": [ - "Member certifications" + "Stocktake" ], "security": [ { @@ -57675,35 +58340,42 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCertificationPage" + "$ref": "#/components/schemas/Stocktake" } } }, "description": "" }, "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenericObject" + } + } + }, + "description": "Invalid request or stocktake state." }, "401": { - "description": "Not signed in." + "description": "Authentication credentials were not provided." }, "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + "description": "Permission denied." }, "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "description": "Not found." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/collaborative-events/": { - "get": { - "operationId": "api_v1_member_makerspaces_collaborative_events_list", - "summary": "List events hosted by accepted collaborators", + "/api/v1/admin/stocktakes/{id}/count-lines": { + "post": { + "operationId": "api_v1_admin_stocktakes_count_lines_create", + "summary": "Count stocktake line", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -57711,22 +58383,39 @@ } ], "tags": [ - "Member events" + "Stocktake" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StocktakeLineInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StocktakeLineInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StocktakeLineInput" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CollaborativeEvent" - } + "$ref": "#/components/schemas/StocktakeLine" } } }, @@ -57736,39 +58425,28 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "Invalid event request." + "description": "Invalid request or stocktake state." + }, + "401": { + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Active membership is required." + "description": "Permission denied." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Collaborative event not found." + "description": "Not found." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/collaborative-events/{id}/register/": { + "/api/v1/admin/stocktakes/{id}/resolve-scan": { "post": { - "operationId": "api_v1_member_makerspaces_collaborative_events_register_create", - "summary": "Register for a collaborative event", + "operationId": "api_v1_admin_stocktakes_resolve_scan_create", + "summary": "Resolve a scanned QR to a stocktake count target", "parameters": [ { "in": "path", @@ -57777,37 +58455,30 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Member events" + "Stocktake" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CollaborativeEventRegistrationInput" + "$ref": "#/components/schemas/StocktakeScanInput" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/CollaborativeEventRegistrationInput" + "$ref": "#/components/schemas/StocktakeScanInput" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/CollaborativeEventRegistrationInput" + "$ref": "#/components/schemas/StocktakeScanInput" } } - } + }, + "required": true }, "security": [ { @@ -57815,11 +58486,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicEventRegistrationResponse" + "$ref": "#/components/schemas/StocktakeScanResult" } } }, @@ -57829,79 +58500,95 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/GenericObject" } } }, - "description": "Invalid event request." + "description": "Invalid request or stocktake state." + }, + "401": { + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Active membership is required." + "description": "Permission denied." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "description": "Not found." + } + } + } + }, + "/api/v1/admin/users/{id}/reset-password": { + "post": { + "operationId": "api_v1_admin_users_reset_password_create", + "summary": "Reset a staff user's password (temp + force change)", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin users" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResetPasswordRequest" } }, - "description": "Collaborative event not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ResetPasswordRequest" } }, - "description": "Event state conflict." - }, - "429": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ResetPasswordRequest" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ResetPasswordResponse" } } }, - "description": "Rate limit exceeded." + "description": "" } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/directory": { - "get": { - "operationId": "api_v1_member_makerspaces_directory_retrieve", - "summary": "List members who published a profile", + "/api/v1/admin/users/{id}/restore-access": { + "post": { + "operationId": "api_v1_admin_users_restore_access_create", + "summary": "Restore user access", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, "required": true - }, - { - "in": "query", - "name": "q", - "schema": { - "type": "string" - }, - "description": "Match on username, display name, headline or institution. Contact details are never searched." } ], "tags": [ - "Member profile" + "Admin users" ], "security": [ { @@ -57913,62 +58600,97 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Directory" + "$ref": "#/components/schemas/User" } } }, "description": "" - }, - "401": { - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + } + } + } + }, + "/api/v1/admin/users/{id}/restrict": { + "post": { + "operationId": "api_v1_admin_users_restrict_create", + "summary": "Restrict or suspend a user", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin users" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RestrictUser" + }, + "examples": { + "RestrictARequester": { + "value": { + "status": "restricted", + "reason": "Unreturned loan under review" + }, + "summary": "Restrict a requester" } } }, - "description": "An active membership is required." + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/RestrictUser" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RestrictUser" + } + } }, - "404": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/User" } } }, - "description": "Not found." + "description": "" } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/directory/{membership_id}": { + "/api/v1/admin/users/booking-managers": { "get": { - "operationId": "api_v1_member_makerspaces_directory_retrieve_2", - "summary": "Retrieve another member's published profile", + "operationId": "api_v1_admin_users_booking_managers_list", + "summary": "List or create staff memberships", "parameters": [ { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "membership_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Member profile" + "Admin users" ], "security": [ { @@ -57980,53 +58702,76 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileRead" + "$ref": "#/components/schemas/PaginatedStaffMembershipList" } } }, "description": "" - }, - "401": { - "description": "Authentication required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + } + } + }, + "post": { + "operationId": "api_v1_admin_users_booking_managers_create", + "summary": "List or create staff memberships", + "tags": [ + "Admin users" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" } }, - "description": "An active membership is required." + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + } }, - "404": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/StaffMembership" } } }, - "description": "Not found." + "description": "" } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/event-calendar-feed/": { + "/api/v1/admin/users/event-managers": { "get": { - "operationId": "api_v1_member_makerspaces_event_calendar_feed_retrieve", + "operationId": "api_v1_admin_users_event_managers_list", + "summary": "List or create staff memberships", "parameters": [ { - "in": "path", - "name": "makerspace_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Member events" + "Admin users" ], "security": [ { @@ -58038,84 +58783,197 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCalendarFeedState" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaginatedStaffMembershipList" } } }, "description": "" + } + } + }, + "post": { + "operationId": "api_v1_admin_users_event_managers_create", + "summary": "List or create staff memberships", + "tags": [ + "Admin users" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + } }, - "403": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/StaffMembership" } } }, "description": "" - }, - "404": { + } + } + } + }, + "/api/v1/admin/users/handover": { + "get": { + "operationId": "api_v1_admin_users_handover_list", + "summary": "List or create staff memberships", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Admin users" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaginatedStaffMembershipList" } } }, "description": "" + } + } + }, + "post": { + "operationId": "api_v1_admin_users_handover_create", + "summary": "List or create staff memberships", + "tags": [ + "Admin users" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + } }, - "400": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/StaffMembership" } } }, "description": "" } } - }, - "post": { - "operationId": "api_v1_member_makerspaces_event_calendar_feed_create", + } + }, + "/api/v1/admin/users/inventory-managers": { + "get": { + "operationId": "api_v1_admin_users_inventory_managers_list", + "summary": "List or create staff memberships", "parameters": [ { - "in": "path", - "name": "makerspace_id", + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", "schema": { "type": "integer" - }, - "required": true + } } ], "tags": [ - "Member events" + "Admin users" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedStaffMembershipList" + } + } + }, + "description": "" + } + } + }, + "post": { + "operationId": "api_v1_admin_users_inventory_managers_create", + "summary": "List or create staff memberships", + "tags": [ + "Admin users" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCalendarFeedIssue" + "$ref": "#/components/schemas/StaffMembership" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberCalendarFeedIssue" + "$ref": "#/components/schemas/StaffMembership" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberCalendarFeedIssue" + "$ref": "#/components/schemas/StaffMembership" } } }, @@ -58127,74 +58985,189 @@ } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCalendarFeedIssued" + "$ref": "#/components/schemas/StaffMembership" } } }, "description": "" - }, - "400": { + } + } + } + }, + "/api/v1/admin/users/machine-managers": { + "get": { + "operationId": "api_v1_admin_users_machine_managers_list", + "summary": "List or create staff memberships", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Admin users" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaginatedStaffMembershipList" } } }, "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + } + } + }, + "post": { + "operationId": "api_v1_admin_users_machine_managers_create", + "summary": "List or create staff memberships", + "tags": [ + "Admin users" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" } }, - "description": "" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + } }, - "403": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/StaffMembership" } } }, "description": "" - }, - "404": { + } + } + } + }, + "/api/v1/admin/users/space-managers": { + "get": { + "operationId": "api_v1_admin_users_space_managers_list", + "summary": "List or create staff memberships", + "parameters": [ + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Admin users" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PaginatedStaffMembershipList" } } }, "description": "" + } + } + }, + "post": { + "operationId": "api_v1_admin_users_space_managers_create", + "summary": "List or create staff memberships", + "tags": [ + "Admin users" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/StaffMembership" + } + } }, - "409": { + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/StaffMembership" } } }, - "description": "Concurrent rotation conflict." + "description": "" } } - }, - "delete": { - "operationId": "api_v1_member_makerspaces_event_calendar_feed_destroy", + } + }, + "/api/v1/admin/warranty/{id}/documents": { + "get": { + "operationId": "api_v1_admin_warranty_documents_list", + "summary": "List documents attached to a warranty", "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "id", "schema": { "type": "integer" }, @@ -58202,7 +59175,7 @@ } ], "tags": [ - "Member events" + "Admin warranty" ], "security": [ { @@ -58210,20 +59183,20 @@ } ], "responses": { - "204": { - "description": "No response body" - }, - "401": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "array", + "items": { + "$ref": "#/components/schemas/WarrantyDocument" + } } } }, "description": "" }, - "403": { + "401": { "content": { "application/json": { "schema": { @@ -58233,7 +59206,7 @@ }, "description": "" }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -58243,7 +59216,7 @@ }, "description": "" }, - "400": { + "404": { "content": { "application/json": { "schema": { @@ -58254,11 +59227,10 @@ "description": "" } } - } - }, - "/api/v1/member/makerspaces/{makerspace_id}/event-certificates/{id}/download/": { - "get": { - "operationId": "api_v1_member_makerspaces_event_certificates_download_retrieve", + }, + "post": { + "operationId": "api_v1_admin_warranty_documents_create", + "summary": "Finalize an uploaded warranty document", "parameters": [ { "in": "path", @@ -58267,36 +59239,51 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Member events" + "Admin warranty" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WarrantyDocumentFinalize" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WarrantyDocumentFinalize" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WarrantyDocumentFinalize" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CertificateDownload" + "$ref": "#/components/schemas/WarrantyDocument" } } }, "description": "" }, - "410": { + "400": { + "description": "Invalid or duplicate warranty document." + }, + "401": { "content": { "application/json": { "schema": { @@ -58304,9 +59291,9 @@ } } }, - "description": "Certificate revoked." + "description": "" }, - "503": { + "403": { "content": { "application/json": { "schema": { @@ -58314,9 +59301,9 @@ } } }, - "description": "Certificate storage unavailable." + "description": "" }, - "400": { + "404": { "content": { "application/json": { "schema": { @@ -58324,19 +59311,71 @@ } } }, - "description": "Invalid feedback answers." + "description": "" }, - "403": { + "503": { + "description": "Warranty document storage is unavailable." + } + } + } + }, + "/api/v1/admin/warranty/{id}/documents/presign": { + "post": { + "operationId": "api_v1_admin_warranty_documents_presign_create", + "summary": "Create a warranty document upload URL", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Admin warranty" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WarrantyDocumentPresign" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/WarrantyDocumentPresign" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/WarrantyDocumentPresign" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/WarrantyDocumentUploadResponse" } } }, - "description": "Active membership is required." + "description": "" }, - "404": { + "400": { + "description": "Invalid document upload request." + }, + "401": { "content": { "application/json": { "schema": { @@ -58344,9 +59383,9 @@ } } }, - "description": "Feedback resource not found." + "description": "" }, - "409": { + "403": { "content": { "application/json": { "schema": { @@ -58354,9 +59393,9 @@ } } }, - "description": "Feedback or certificate state conflict." + "description": "" }, - "429": { + "404": { "content": { "application/json": { "schema": { @@ -58364,16 +59403,18 @@ } } }, - "description": "Rate limit exceeded." + "description": "" + }, + "503": { + "description": "Warranty document storage is unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/cancel/": { - "post": { - "operationId": "api_v1_member_makerspaces_event_registrations_cancel_create", - "description": "Releases pending charges and uses the event's existing waitlist promotion policy. Settled charges are retained; this action does not issue a refund. Members may cancel only before the event's cancellation cutoff (null means start).", - "summary": "Cancel the caller's event registration", + "/api/v1/admin/warranty/documents/{id}": { + "delete": { + "operationId": "api_v1_admin_warranty_documents_destroy", + "summary": "Delete a warranty document", "parameters": [ { "in": "path", @@ -58382,18 +59423,10 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Member events" + "Admin warranty" ], "security": [ { @@ -58401,28 +59434,30 @@ } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberRegistrationCancelResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "403": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid body or events module disabled (field-keyed errors)." + "description": "" }, - "401": { + "404": { "content": { "application/json": { "schema": { @@ -58430,74 +59465,15 @@ } } }, - "description": "Authentication is required." - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "An active account owning an account/invite registration, or an active membership, is required." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Owned registration not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Registration cannot be cancelled." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Tenant frozen for migration." - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Tenant write gate unavailable." + "description": "" } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/feedback/": { + "/api/v1/admin/warranty/documents/{id}/url": { "get": { - "operationId": "api_v1_member_makerspaces_event_registrations_feedback_retrieve", + "operationId": "api_v1_admin_warranty_documents_url_retrieve", + "summary": "Create a signed warranty document view URL", "parameters": [ { "in": "path", @@ -58506,18 +59482,10 @@ "type": "integer" }, "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true } ], "tags": [ - "Member events" + "Admin warranty" ], "security": [ { @@ -58529,13 +59497,13 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackForm" + "$ref": "#/components/schemas/WarrantyDocumentUrl" } } }, "description": "" }, - "400": { + "401": { "content": { "application/json": { "schema": { @@ -58543,7 +59511,7 @@ } } }, - "description": "Invalid feedback answers." + "description": "" }, "403": { "content": { @@ -58553,7 +59521,7 @@ } } }, - "description": "Active membership is required." + "description": "" }, "404": { "content": { @@ -58563,71 +59531,76 @@ } } }, - "description": "Feedback resource not found." + "description": "" }, - "409": { + "503": { + "description": "Warranty document storage is unavailable." + } + } + } + }, + "/api/v1/auth/account/deletion": { + "get": { + "operationId": "api_v1_auth_account_deletion_retrieve", + "description": "Preview global obligations and available fresh identity verification methods.", + "tags": [ + "Accounts" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AccountDeletionPreview" } } }, - "description": "Feedback or certificate state conflict." + "description": "" + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } }, "post": { - "operationId": "api_v1_member_makerspaces_event_registrations_feedback_create", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_account_deletion_create", + "description": "Requires session-bound reauthentication within ten minutes. Atomically archives the account and revokes its credentials; retained records follow their retention policies.", "tags": [ - "Member events" + "Accounts" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackSubmission" + "$ref": "#/components/schemas/AccountDeletionRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/FeedbackSubmission" + "$ref": "#/components/schemas/AccountDeletionRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/FeedbackSubmission" + "$ref": "#/components/schemas/AccountDeletionRequest" } } - } + }, + "required": true }, "security": [ { @@ -58635,170 +59608,302 @@ } ], "responses": { - "201": { + "202": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackSubmissionResponse" + "$ref": "#/components/schemas/AccountDeletionResult" } } }, "description": "" }, - "400": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AccountDeletionRefused" } } }, - "description": "Invalid feedback answers." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AccountDeletionForbidden" } } }, - "description": "Active membership is required." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "description": "" + } + } + } + }, + "/api/v1/auth/account/restore/email": { + "post": { + "operationId": "api_v1_auth_account_restore_email_create", + "description": "Consume a single-use, attempt-capped, 24-hour restore link delivered by password recovery. Requires a new password; refuses at the purge deadline or once any purge started.", + "tags": [ + "Accounts" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreEmail" } }, - "description": "Feedback resource not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreEmail" } }, - "description": "Feedback or certificate state conflict." + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreEmail" + } + } }, - "429": { + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AccountRestoreResult" } } }, - "description": "Rate limit exceeded." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/qr": { - "get": { - "operationId": "api_v1_member_makerspaces_event_registrations_qr_retrieve", - "summary": "Render the caller's event check-in QR code", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/auth/account/restore/phone": { + "post": { + "operationId": "api_v1_auth_account_restore_phone_create", + "description": "Consume a restore OTP from phone/login/start for an archived phone-only account. Device delivery must prove the exact attestation challenge. Requires a new password.", "tags": [ - "Member activity" - ], - "security": [ - { - "jwtAuth": [] - } + "Accounts" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountRestorePhone" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/AccountRestorePhone" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/AccountRestorePhone" + } + } + }, + "required": true + }, "responses": { "200": { "content": { - "image/svg+xml": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/AccountRestoreResult" } } }, - "description": "Check-in QR code as SVG." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "409": { + "description": "Conflicts with the current state." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/auth/account/restore/social/complete": { + "post": { + "operationId": "api_v1_auth_account_restore_social_complete_create", + "description": "Fresh auth_time within five minutes is mandatory for Apple, Google and OIDC. OIDC authorization requests prompt=login and max_age=0. Providers without that proof cannot restore. Account eligibility and the original linked subject are rechecked under lock.", + "tags": [ + "Accounts" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreSocialComplete" } }, - "description": "Active membership or an owned account/invite registration is required." + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreSocialComplete" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreSocialComplete" + } + } }, - "400": { + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AccountRestoreResult" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/auth/account/restore/social/start": { + "post": { + "operationId": "api_v1_auth_account_restore_social_start_create", + "description": "Start a five-minute, single-use fresh sign-in for an already-linked provider. Browser delivery requires a registered member origin and same-origin redirect. Native Apple/Google delivery binds an approved app attestation challenge and audience. Only accounts without email or phone can restore through this flow. Never links identities.", + "tags": [ + "Accounts" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreSocialStart" } }, - "description": "" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreSocialStart" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/AccountRestoreSocialStart" + } + } }, - "404": { + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/AccountRestoreSocialResult" } } }, - "description": "Registration not found." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/calendar.ics": { - "get": { - "operationId": "api_v1_member_makerspaces_event_registrations_calendar.ics_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/auth/change-password": { + "post": { + "operationId": "api_v1_auth_change_password_create", + "summary": "Change current user's password", "tags": [ - "Member events" + "Auth" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChangePassword" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ChangePassword" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ChangePassword" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -58807,26 +59912,62 @@ "responses": { "200": { "content": { - "text/calendar": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/ChangePasswordResponse" } } }, - "description": "RFC 5545 calendar (`text/calendar; charset=utf-8`)." + "description": "" + }, + "400": { + "description": "Password validation failed." }, "401": { + "description": "Authentication credentials were not provided." + } + } + } + }, + "/api/v1/auth/claim/redeem": { + "post": { + "operationId": "api_v1_auth_claim_redeem_create", + "summary": "Redeem a staff-issued member claim code", + "tags": [ + "Auth" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ClaimRedemption" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ClaimRedemption" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ClaimRedemption" + } + } + }, + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ClaimRedemptionResponse" } } }, - "description": "Authentication required." + "description": "" }, - "403": { + "400": { "content": { "application/json": { "schema": { @@ -58834,17 +59975,54 @@ } } }, - "description": "Active membership required." + "description": "Invalid or expired claim code." }, "404": { + "description": "Makerspace not found." + }, + "429": { + "description": "Redemption rate limit exceeded." + } + } + } + }, + "/api/v1/auth/device/attestation-challenge": { + "post": { + "operationId": "api_v1_auth_device_attestation_challenge_create", + "description": "Challenges require an approved deployment-global registration and snapshot its signing identities. Play Integrity accepts any registered distribution signer and grants retain the matched digest. External verifiers receive only the first signing identity. Submit the returned challenge with the device login attestation payload: Android {\"type\":\"play_integrity\",\"token\":\"...\"} uses Standard requestHash = base64url(SHA-256(challenge UTF-8)), without padding. iOS {\"type\":\"app_attest\",\"key_id\":\"\",\"attestation\":\"\"} uses a fresh key and clientDataHash = SHA-256(challenge UTF-8). Development-insecure mode accepts {\"type\":\"development\"} for development registrations only. External verifiers retain their deployment-specific payload format. Verifier outages return attestation_unavailable (503); restart with a new challenge.", + "tags": [ + "Device auth" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceIdentity" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DeviceIdentity" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DeviceIdentity" + } + } + }, + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceChallengeResponse" } } }, - "description": "Makerspace not found." + "description": "" }, "400": { "content": { @@ -58854,67 +60032,106 @@ } } }, - "description": "Events module unavailable." + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/loans/{id}/renew": { - "post": { - "operationId": "api_v1_member_makerspaces_loans_renew_create", - "summary": "Extend an issued loan within its policy", - "parameters": [ + "/api/v1/auth/device/grants": { + "get": { + "operationId": "api_v1_auth_device_grants_list", + "tags": [ + "Device auth" + ], + "security": [ { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DeviceGrant" + } + } + } }, - "required": true + "description": "" }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/auth/device/grants/{grant_id}": { + "delete": { + "operationId": "api_v1_auth_device_grants_destroy", + "parameters": [ { "in": "path", - "name": "makerspace_id", + "name": "grant_id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Loans" + "Device auth" ], - "requestBody": { - "content": { - "type": { - "schema": { - "type": "object", - "additionalProperties": {}, - "description": "Unspecified request body" - } - }, - "properties": { - "schema": {} - } - } - }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LoanRenewal" + "$ref": "#/components/schemas/HardwareRequestError" } } }, "description": "" }, - "400": { + "404": { "content": { "application/json": { "schema": { @@ -58923,6 +60140,47 @@ } }, "description": "" + } + } + } + }, + "/api/v1/auth/device/login": { + "post": { + "operationId": "api_v1_auth_device_login_create", + "description": "Attested password sign-in. Local staff receive native_staff authority after any required MFA. New superadmin grants are refused with device_superadmin_not_supported (403). Challenges require an approved deployment-global registration and snapshot its signing identities. Play Integrity accepts any registered distribution signer and grants retain the matched digest. External verifiers receive only the first signing identity. Submit the returned challenge with the device login attestation payload: Android {\"type\":\"play_integrity\",\"token\":\"...\"} uses Standard requestHash = base64url(SHA-256(challenge UTF-8)), without padding. iOS {\"type\":\"app_attest\",\"key_id\":\"\",\"attestation\":\"\"} uses a fresh key and clientDataHash = SHA-256(challenge UTF-8). Development-insecure mode accepts {\"type\":\"development\"} for development registrations only. External verifiers retain their deployment-specific payload format. Verifier outages return attestation_unavailable (503); restart with a new challenge.", + "tags": [ + "Device auth" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceLogin" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/DeviceLogin" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/DeviceLogin" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceLoginResult" + } + } + }, + "description": "" }, "401": { "content": { @@ -58944,7 +60202,7 @@ }, "description": "" }, - "404": { + "503": { "content": { "application/json": { "schema": { @@ -58957,22 +60215,11 @@ } } }, - "/api/v1/member/makerspaces/{makerspace_id}/member-card": { - "get": { - "operationId": "api_v1_member_makerspaces_member_card_retrieve", - "summary": "Read my member card", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/auth/device/logout": { + "post": { + "operationId": "api_v1_auth_device_logout_create", "tags": [ - "Member profile" + "Device auth" ], "security": [ { @@ -58984,124 +60231,126 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/OwnMemberCard" + "$ref": "#/components/schemas/DeviceLogoutResponse" } } }, "description": "" }, - "400": { - "description": "Membership module disabled or invalid input." - }, "401": { - "description": "Authentication required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "An active membership is required." - }, - "404": { - "description": "No card has been issued to this membership." - } - } - }, - "patch": { - "operationId": "api_v1_member_makerspaces_member_card_partial_update", - "summary": "Set the name printed on my card", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true + "description": "" } - ], + } + } + }, + "/api/v1/auth/device/refresh": { + "post": { + "operationId": "api_v1_auth_device_refresh_create", "tags": [ - "Member profile" + "Device auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedMemberCardOwnUpdate" + "$ref": "#/components/schemas/DeviceRefresh" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedMemberCardOwnUpdate" + "$ref": "#/components/schemas/DeviceRefresh" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedMemberCardOwnUpdate" + "$ref": "#/components/schemas/DeviceRefresh" } } - } + }, + "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCard" + "$ref": "#/components/schemas/DeviceRefreshResponse" } } }, "description": "" }, "400": { - "description": "Membership module disabled or invalid input." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "401": { - "description": "Authentication required." - }, - "403": { - "description": "An active membership is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "404": { - "description": "No card has been issued to this membership." + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/member-card/photo": { + "/api/v1/auth/email-verification/confirm": { "post": { - "operationId": "api_v1_member_makerspaces_member_card_photo_create", - "summary": "Create a card photo upload URL (staging only)", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_email_verification_confirm_create", "tags": [ - "Member profile" + "Auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardPhotoPresign" + "$ref": "#/components/schemas/EmailVerificationConfirm" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberCardPhotoPresign" + "$ref": "#/components/schemas/EmailVerificationConfirm" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberCardPhotoPresign" + "$ref": "#/components/schemas/EmailVerificationConfirm" } } }, @@ -59113,163 +60362,226 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/MemberVerificationAck" } } }, "description": "" }, "400": { - "description": "Membership module disabled or invalid input." + "description": "Invalid or expired verification code." }, "401": { - "description": "Authentication required." + "description": "Authentication credentials were not provided." }, "403": { - "description": "An active membership is required." - }, - "404": { - "description": "No card has been issued to this membership." + "description": "Permission denied." }, - "503": { - "description": "Storage unavailable." + "429": { + "description": "Request throttled." } } - }, - "put": { - "operationId": "api_v1_member_makerspaces_member_card_photo_update", - "summary": "Attach an uploaded card photo (requires consent)", - "parameters": [ + } + }, + "/api/v1/auth/email-verification/resend": { + "post": { + "operationId": "api_v1_auth_email_verification_resend_create", + "tags": [ + "Auth" + ], + "security": [ { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true + "jwtAuth": [] } ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberVerificationAck" + } + } + }, + "description": "" + }, + "400": { + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "429": { + "description": "Request throttled." + } + } + } + }, + "/api/v1/auth/forgot-password": { + "post": { + "operationId": "api_v1_auth_forgot_password_create", + "description": "Queues the same account-independent request for all addresses. The drain sends eligible archived accounts a single-use, 24-hour restore link instead of password-reset mail.", + "summary": "Request an emailed password reset code", "tags": [ - "Member profile" + "Auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCardPhotoFinalize" + "$ref": "#/components/schemas/ForgotPasswordRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberCardPhotoFinalize" + "$ref": "#/components/schemas/ForgotPasswordRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberCardPhotoFinalize" + "$ref": "#/components/schemas/ForgotPasswordRequest" } } }, "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCard" + "$ref": "#/components/schemas/PasswordResetAcknowledgement" } } }, "description": "" }, "400": { - "description": "Membership module disabled or invalid input." - }, - "401": { - "description": "Authentication required." + "description": "Invalid request." }, - "403": { - "description": "An active membership is required." + "429": { + "description": "Request throttled." }, - "404": { - "description": "No card has been issued to this membership." - } - } - }, - "delete": { - "operationId": "api_v1_member_makerspaces_member_card_photo_destroy", - "summary": "Remove my card photo", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RecoveryUnavailable" + } + } }, - "required": true + "description": "" } - ], + } + } + }, + "/api/v1/auth/login": { + "post": { + "operationId": "api_v1_auth_login_create", + "description": "Takes a set of user credentials and returns an access and refresh JSON web\ntoken pair to prove the authentication of those credentials.", + "summary": "Log in with a password", "tags": [ - "Member profile" - ], - "security": [ - { - "jwtAuth": [] - } + "Auth" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoginRequest" + }, + "examples": { + "StaffLogin": { + "value": { + "username": "admin", + "password": "secret-password", + "surface": "staff" + }, + "summary": "Staff login" + } + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/LoginRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/LoginRequest" + } + } + }, + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberCard" + "$ref": "#/components/schemas/PasswordLoginResult" } } }, "description": "" }, "400": { - "description": "Membership module disabled or invalid input." + "description": "Invalid request." }, "401": { - "description": "Authentication required." + "description": "Invalid credentials or inactive account." }, "403": { - "description": "An active membership is required." + "description": "Account access is restricted." }, - "404": { - "description": "No card has been issued to this membership." + "429": { + "description": "Request throttled." + }, + "503": { + "description": "Authentication write boundary unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/member-card/preview.pdf": { - "get": { - "operationId": "api_v1_member_makerspaces_member_card_preview.pdf_retrieve", - "summary": "Preview my card as a watermarked PDF", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" + "/api/v1/auth/logout": { + "post": { + "operationId": "api_v1_auth_logout_create", + "summary": "Log out and clear refresh cookie", + "tags": [ + "Auth" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LogoutResponse" + } + } }, - "required": true + "description": "" + }, + "401": { + "description": "Refresh token could not be blacklisted." + }, + "403": { + "description": "CSRF check failed." } - ], + } + } + }, + "/api/v1/auth/logout-all": { + "post": { + "operationId": "api_v1_auth_logout_all_create", + "summary": "Sign out of every browser and device session", "tags": [ - "Member profile" + "Auth" ], "security": [ { @@ -59279,101 +60591,25 @@ "responses": { "200": { "content": { - "application/pdf": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/LogoutAllResponse" } } }, "description": "" }, - "400": { - "description": "Membership module disabled or invalid input." - }, "401": { - "description": "Authentication required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "An active membership is required." - }, - "404": { - "description": "No card has been issued to this membership." - } - } - } - }, - "/api/v1/member/makerspaces/{makerspace_id}/payment-documents": { - "get": { - "operationId": "api_v1_member_makerspaces_payment_documents_retrieve", - "summary": "List scoped payment documents", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - } - ], - "tags": [ - "Payments" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DocumentPage" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { "content": { "application/json": { "schema": { @@ -59386,30 +60622,12 @@ } } }, - "/api/v1/member/makerspaces/{makerspace_id}/payment-documents/{document_id}/download-url": { + "/api/v1/auth/me": { "get": { - "operationId": "api_v1_member_makerspaces_payment_documents_download_url_retrieve", - "summary": "Get a user-bound five-minute PDF download URL", - "parameters": [ - { - "in": "path", - "name": "document_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_me_retrieve", + "summary": "Get current session profile", "tags": [ - "Payments" + "Auth" ], "security": [ { @@ -59421,161 +60639,84 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DownloadGrant" + "$ref": "#/components/schemas/AuthUserPayload" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Invalid request." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Authentication credentials were not provided." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Permission denied." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "429": { + "description": "Request throttled." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/payments": { - "get": { - "operationId": "api_v1_member_makerspaces_payments_list", - "description": "Active memberships and account-only charge owners may read their own history. A revoked member reapplying through an approved admission may read only that application's pending admission charge; previous membership debt stays refused.", - "summary": "List the caller's payment history", - "parameters": [ - { - "in": "query", - "name": "due_state", - "schema": { - "type": "string", - "enum": [ - "due_today", - "overdue", - "upcoming" - ] - } - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/auth/member-sign-up": { + "post": { + "operationId": "api_v1_auth_member_sign_up_create", "tags": [ - "Payments" - ], - "security": [ - { - "jwtAuth": [] - } + "Auth" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberSignUp" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberSignUp" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberSignUp" + } + } + }, + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberPayment" - } + "$ref": "#/components/schemas/MemberVerificationAck" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaymentFilterError" - } - } - }, - "description": "" + "description": "Invalid details." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Authentication failed." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "No active membership and no charge here." + "description": "Permission denied." + }, + "429": { + "description": "Request throttled." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/payments/{payment_id}/checkout": { - "post": { - "operationId": "api_v1_member_makerspaces_payments_checkout_create", - "summary": "Generate a Checkout link for the caller's pending payment", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "payment_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/auth/mfa": { + "get": { + "operationId": "api_v1_auth_mfa_retrieve", "tags": [ - "Payments" + "MFA" ], "security": [ { @@ -59587,80 +60728,47 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CheckoutUrl" + "$ref": "#/components/schemas/MFAStatus" } } }, "description": "" }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "No pending charge of yours here." + "description": "Not signed in." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "`payment_rail_conflict`: the charge uses the native rail." + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "`payments_unavailable`: no live online rail for this charge." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." } } - } - }, - "/api/v1/member/makerspaces/{makerspace_id}/payments/{payment_id}/mobile-intent": { + }, "post": { - "operationId": "api_v1_member_makerspaces_payments_mobile_intent_create", - "summary": "Create or retrieve a native mobile payment intent", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" + "operationId": "api_v1_auth_mfa_create", + "tags": [ + "MFA" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MFAEnrollmentStart" + } }, - "required": true - }, - { - "in": "path", - "name": "payment_id", - "schema": { - "type": "integer" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MFAEnrollmentStart" + } }, - "required": true + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MFAEnrollmentStart" + } + } } - ], - "tags": [ - "Payments" - ], + }, "security": [ { "jwtAuth": [] @@ -59671,157 +60779,111 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MobilePaymentIntentResponse" + "$ref": "#/components/schemas/MFAChallengeResponse" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "A required dependency is unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/profile": { - "get": { - "operationId": "api_v1_member_makerspaces_profile_retrieve", - "summary": "Retrieve the caller's own profile", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/auth/mfa/confirm": { + "post": { + "operationId": "api_v1_auth_mfa_confirm_create", + "description": "Complete the server-owned login context. New device grants retain login provenance and derive native_staff authority only for password/social local staff. New superadmin grants return device_superadmin_not_supported (403). Attestation outages return attestation_unavailable (503).", "tags": [ - "Member profile" - ], - "security": [ - { - "jwtAuth": [] - } + "MFA" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MFAVerify" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MFAVerify" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MFAVerify" + } + } + }, + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileRead" + "$ref": "#/components/schemas/MFACompleted" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, "401": { - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "An active membership is required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } - }, - "put": { - "operationId": "api_v1_member_makerspaces_profile_update", - "summary": "Update the caller's own profile", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + } + }, + "/api/v1/auth/mfa/disable": { + "post": { + "operationId": "api_v1_auth_mfa_disable_create", "tags": [ - "Member profile" + "MFA" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileWrite" + "$ref": "#/components/schemas/MFAPassword" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ProfileWrite" + "$ref": "#/components/schemas/MFAPassword" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ProfileWrite" + "$ref": "#/components/schemas/MFAPassword" } } - } + }, + "required": true }, "security": [ { @@ -59833,136 +60895,106 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileRead" + "$ref": "#/components/schemas/MFAStatus" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, "401": { - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "An active membership is required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/profile/image": { + "/api/v1/auth/mfa/enroll": { "post": { - "operationId": "api_v1_member_makerspaces_profile_image_create", - "summary": "Create a profile image upload URL", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_mfa_enroll_create", "tags": [ - "Member profile" + "MFA" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileImageUploadRequest" + "$ref": "#/components/schemas/MFAChallenge" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ProfileImageUploadRequest" + "$ref": "#/components/schemas/MFAChallenge" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ProfileImageUploadRequest" + "$ref": "#/components/schemas/MFAChallenge" } } }, "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicImageUploadResponse" + "$ref": "#/components/schemas/MFAEnrollment" } } }, "description": "" }, "400": { - "description": "Invalid image upload request." + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." }, "403": { - "description": "An active membership is required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "429": { - "description": "Too many image upload requests." + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, "503": { - "description": "Public image storage is unavailable." + "description": "A required dependency is unavailable." } } - }, - "put": { - "operationId": "api_v1_member_makerspaces_profile_image_update", - "summary": "Attach an uploaded image to the profile or one of its projects", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + } + }, + "/api/v1/auth/mfa/recovery": { + "post": { + "operationId": "api_v1_auth_mfa_recovery_create", "tags": [ - "Member profile" + "MFA" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileImageAttachRequest" + "$ref": "#/components/schemas/MFAPassword" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ProfileImageAttachRequest" + "$ref": "#/components/schemas/MFAPassword" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ProfileImageAttachRequest" + "$ref": "#/components/schemas/MFAPassword" } } }, @@ -59978,200 +61010,108 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ProfileRead" + "$ref": "#/components/schemas/MFARecovery" } } }, "description": "" }, "400": { - "description": "Invalid image object key or size." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, - "403": { - "description": "An active membership is required." + "401": { + "description": "Not signed in." }, - "503": { - "description": "Public image storage is unavailable." - } - } - }, - "delete": { - "operationId": "api_v1_member_makerspaces_profile_image_destroy", - "summary": "Clear a profile or project image", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Member profile" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProfileRead" - } - } - }, - "description": "" + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "400": { - "description": "Unknown project." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, - "403": { - "description": "An active membership is required." + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/referrals": { + "/api/v1/auth/mfa/verify": { "post": { - "operationId": "api_v1_member_makerspaces_referrals_create", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_mfa_verify_create", + "description": "Complete the server-owned login context. New device grants retain login provenance and derive native_staff authority only for password/social local staff. New superadmin grants return device_superadmin_not_supported (403). Attestation outages return attestation_unavailable (503).", "tags": [ - "Memberships" + "MFA" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReferralCreate" + "$ref": "#/components/schemas/MFAVerify" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ReferralCreate" + "$ref": "#/components/schemas/MFAVerify" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ReferralCreate" + "$ref": "#/components/schemas/MFAVerify" } } }, "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReferralOutcome" + "$ref": "#/components/schemas/MFACompleted" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/member/makerspaces/{makerspace_id}/verification": { + "/api/v1/auth/organization-invitations/redeem/": { "post": { - "operationId": "api_v1_member_makerspaces_verification_create", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_organization_invitations_redeem_create", + "summary": "Redeem a single-use organization invitation", "tags": [ - "Memberships" + "Auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberVerificationRequest" + "$ref": "#/components/schemas/OrganizationInvitationRedeem" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberVerificationRequest" + "$ref": "#/components/schemas/OrganizationInvitationRedeem" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberVerificationRequest" + "$ref": "#/components/schemas/OrganizationInvitationRedeem" } } }, @@ -60187,21 +61127,14 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberVerificationResponse" + "$ref": "#/components/schemas/OrganizationInvitationRedeemed" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Malformed token." }, "401": { "content": { @@ -60224,6 +61157,9 @@ "description": "" }, "404": { + "description": "Invitation not found." + }, + "409": { "content": { "application/json": { "schema": { @@ -60236,21 +61172,11 @@ } } }, - "/api/v1/member/makerspaces/{makerspace_id}/waiver": { + "/api/v1/auth/organizations/self-service/": { "get": { - "operationId": "api_v1_member_makerspaces_waiver_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_organizations_self_service_retrieve", "tags": [ - "Memberships" + "Auth" ], "security": [ { @@ -60262,17 +61188,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberWaiverResponse" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/OrganizationCreationPolicy" } } }, @@ -60282,102 +61198,106 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" + "description": "Not signed in." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" + "description": "Rate limit exceeded; retry after the interval in Retry-After." } } - } - }, - "/api/v1/member/makerspaces/{makerspace_id}/waiver/accept": { + }, "post": { - "operationId": "api_v1_member_makerspaces_waiver_accept_create", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_organizations_self_service_create", "tags": [ - "Memberships" + "Auth" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OrganizationSelfCreate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/OrganizationSelfCreate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/OrganizationSelfCreate" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WaiverAcceptResponse" + "$ref": "#/components/schemas/OrganizationDetail" } } }, @@ -60387,119 +61307,175 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" + "description": "Not signed in." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" + "description": "Conflicts with the current state." }, "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": true, + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + }, + "purpose": { + "type": "string" + } + } } } }, - "description": "" - }, - "503": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + } + } + } + }, + "/api/v1/auth/phone": { + "delete": { + "operationId": "api_v1_auth_phone_destroy", + "description": "Detach the number. Always safe: every account keeps an email credential.", + "summary": "Unlink the phone number", + "tags": [ + "Auth" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PhoneStatus" } } }, "description": "" + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." } } } }, - "/api/v1/memberships/{id}/accept-invitation": { + "/api/v1/auth/phone/link/confirm": { "post": { - "operationId": "api_v1_memberships_accept_invitation_create", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_phone_link_confirm_create", + "description": "Attach the verified number to the caller's account.", + "summary": "Confirm and link a phone number", "tags": [ - "Memberships" + "Auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdmissionAnswers" + "$ref": "#/components/schemas/PhoneConfirm" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/AdmissionAnswers" + "$ref": "#/components/schemas/PhoneConfirm" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/AdmissionAnswers" + "$ref": "#/components/schemas/PhoneConfirm" } } - } + }, + "required": true }, "security": [ { @@ -60511,81 +61487,49 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InvitationClaimOutcome" + "$ref": "#/components/schemas/PhoneStatus" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Invalid or expired code." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "429": { + "description": "Request throttled." } } } }, - "/api/v1/memberships/invitations": { - "get": { - "operationId": "api_v1_memberships_invitations_retrieve", + "/api/v1/auth/phone/link/start": { + "post": { + "operationId": "api_v1_auth_phone_link_start_create", + "description": "Send a code to a number the caller wants to attach to their own account.", + "summary": "Request a code to link a phone number", "tags": [ - "Memberships" + "Auth" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PhoneStart" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PhoneStart" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PhoneStart" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -60596,145 +61540,68 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InvitationList" + "$ref": "#/components/schemas/PhoneStartResponse" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Invalid or already-linked number." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Phone sign-in is not configured." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "429": { + "description": "Request throttled." } } } }, - "/api/v1/memberships/invitations/{id}/claim": { + "/api/v1/auth/phone/login/confirm": { "post": { - "operationId": "api_v1_memberships_invitations_claim_create", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_phone_login_confirm_create", + "description": "Device delivery creates a new member-only grant, or returns an MFA challenge when required, without setting cookies. Superadmins receive device_superadmin_not_supported (403). Rejected or consumed device challenges return phone_device_restart_required (409). Challenges require an approved deployment-global registration and snapshot its signing identities. Play Integrity accepts any registered distribution signer and grants retain the matched digest. External verifiers receive only the first signing identity. Submit the returned challenge with the device login attestation payload: Android {\"type\":\"play_integrity\",\"token\":\"...\"} uses Standard requestHash = base64url(SHA-256(challenge UTF-8)), without padding. iOS {\"type\":\"app_attest\",\"key_id\":\"\",\"attestation\":\"\"} uses a fresh key and clientDataHash = SHA-256(challenge UTF-8). Development-insecure mode accepts {\"type\":\"development\"} for development registrations only. External verifiers retain their deployment-specific payload format. Verifier outages return attestation_unavailable (503); restart with a new challenge.", + "summary": "Sign in with a phone code", "tags": [ - "Memberships" + "Auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AdmissionAnswers" + "$ref": "#/components/schemas/PhoneLoginConfirm" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/AdmissionAnswers" + "$ref": "#/components/schemas/PhoneLoginConfirm" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/AdmissionAnswers" + "$ref": "#/components/schemas/PhoneLoginConfirm" } } - } + }, + "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InvitationClaimOutcome" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PhoneLoginResult" } } }, "description": "" }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "The first-factor account is no longer available." + }, + "400": { + "description": "Invalid or expired code." }, "403": { "content": { @@ -60744,19 +61611,12 @@ } } }, - "description": "" + "description": "device_superadmin_not_supported: superadmins cannot create grants." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Phone sign-in is not configured." }, - "423": { + "409": { "content": { "application/json": { "schema": { @@ -60764,7 +61624,10 @@ } } }, - "description": "" + "description": "phone_device_restart_required: restart with new one-time inputs." + }, + "429": { + "description": "Request throttled." }, "503": { "content": { @@ -60774,169 +61637,67 @@ } } }, - "description": "" + "description": "attestation_unavailable or authentication write boundary unavailable." } } } }, - "/api/v1/memberships/me": { - "get": { - "operationId": "api_v1_memberships_me_retrieve", + "/api/v1/auth/phone/login/start": { + "post": { + "operationId": "api_v1_auth_phone_login_start_create", + "description": "Archived phone-only accounts receive a restore OTP instead of a login OTP, redeemable at account/restore/phone. Device delivery binds the OTP to an approved attestation challenge and matching ios/android client_platform. Unknown numbers, invalid challenges and cooldowns receive the same acknowledgement.", + "summary": "Request a phone sign-in code", "tags": [ - "Memberships" - ], - "security": [ - { - "jwtAuth": [] - } + "Auth" ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MyMemberships" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PhoneLoginStart" } }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PhoneLoginStart" } }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PhoneLoginStart" } - }, - "description": "" + } }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - } - } - } - }, - "/api/v1/metrics/": { - "get": { - "operationId": "api_v1_metrics_retrieve", - "description": "Prometheus text exposition of queue depth, request states, notification delivery outcomes, storage accounting and evidence retention. Requires the deployment's METRICS_TOKEN as a bearer token; 404 when no token is configured.", - "summary": "Prometheus metrics", - "tags": [ - "Health" - ], - "security": [ - {} - ], + "required": true + }, "responses": { "200": { "content": { - "text/plain": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/PhoneStartResponse" } } }, "description": "" }, - "401": { - "description": "Missing or wrong METRICS_TOKEN." + "400": { + "description": "Invalid request." }, "404": { - "description": "METRICS_TOKEN is not set." + "description": "Phone sign-in is not configured." + }, + "429": { + "description": "Request throttled." } } } }, - "/api/v1/notifications/announcements/{makerspace_id}": { + "/api/v1/auth/reauth": { "get": { - "operationId": "api_v1_notifications_announcements_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - } - ], + "operationId": "api_v1_auth_reauth_retrieve", "tags": [ - "Member communication" + "Reauthentication" ], "security": [ { @@ -60948,7 +61709,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InboxPage" + "$ref": "#/components/schemas/ReauthenticationStatus" } } }, @@ -60963,28 +61724,38 @@ "403": { "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } - } - }, - "/api/v1/notifications/broadcasts/{makerspace_id}": { - "get": { - "operationId": "api_v1_notifications_broadcasts_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + }, + "post": { + "operationId": "api_v1_auth_reauth_create", "tags": [ - "Member communication" + "Reauthentication" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Reauthentication" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Reauthentication" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Reauthentication" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -60995,7 +61766,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BroadcastOptions" + "$ref": "#/components/schemas/ReauthenticationResult" } } }, @@ -61010,41 +61781,36 @@ "403": { "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } - }, + } + }, + "/api/v1/auth/reauth/enrollment/confirm": { "post": { - "operationId": "api_v1_notifications_broadcasts_create", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_reauth_enrollment_confirm_create", "tags": [ - "Member communication" + "Reauthentication" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BroadcastInput" + "$ref": "#/components/schemas/ReauthenticationEmailConfirm" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/BroadcastInput" + "$ref": "#/components/schemas/ReauthenticationEmailConfirm" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/BroadcastInput" + "$ref": "#/components/schemas/ReauthenticationEmailConfirm" } } }, @@ -61056,11 +61822,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BroadcastResult" + "$ref": "#/components/schemas/ReauthenticationEmailConfirmed" } } }, @@ -61075,11 +61841,8 @@ "403": { "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, "503": { "description": "A required dependency is unavailable." @@ -61087,55 +61850,11 @@ } } }, - "/api/v1/notifications/inbox/{makerspace_id}": { - "get": { - "operationId": "api_v1_notifications_inbox_retrieve", - "parameters": [ - { - "in": "query", - "name": "feature", - "schema": { - "type": "string", - "enum": [ - "api_access", - "bookings", - "certifications", - "devices", - "events", - "hardware_requests", - "maintenance", - "members", - "payments", - "printing", - "stock" - ] - } - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "unread", - "schema": { - "type": "boolean" - } - } - ], + "/api/v1/auth/reauth/enrollment/email": { + "post": { + "operationId": "api_v1_auth_reauth_enrollment_email_create", "tags": [ - "Personal notifications" + "Reauthentication" ], "security": [ { @@ -61147,7 +61866,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InboxPage" + "$ref": "#/components/schemas/ReauthenticationEmailSent" } } }, @@ -61162,36 +61881,41 @@ "403": { "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/notifications/inbox/{makerspace_id}/{id}/read": { + "/api/v1/auth/reauth/phone/confirm": { "post": { - "operationId": "api_v1_notifications_inbox_read_create", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_reauth_phone_confirm_create", "tags": [ - "Personal notifications" + "Reauthentication" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationFactor" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationFactor" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationFactor" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -61202,7 +61926,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Inbox" + "$ref": "#/components/schemas/ReauthenticationResult" } } }, @@ -61217,11 +61941,8 @@ "403": { "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, "503": { "description": "A required dependency is unavailable." @@ -61229,21 +61950,11 @@ } } }, - "/api/v1/notifications/inbox/{makerspace_id}/read-all": { + "/api/v1/auth/reauth/phone/start": { "post": { - "operationId": "api_v1_notifications_inbox_read_all_create", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_reauth_phone_start_create", "tags": [ - "Personal notifications" + "Reauthentication" ], "security": [ { @@ -61255,7 +61966,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReadAll" + "$ref": "#/components/schemas/ReauthenticationPhoneStarted" } } }, @@ -61270,11 +61981,8 @@ "403": { "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, "503": { "description": "A required dependency is unavailable." @@ -61282,22 +61990,32 @@ } } }, - "/api/v1/notifications/inbox/{makerspace_id}/unread-count": { - "get": { - "operationId": "api_v1_notifications_inbox_unread_count_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/auth/reauth/social/complete": { + "post": { + "operationId": "api_v1_auth_reauth_social_complete_create", "tags": [ - "Personal notifications" + "Reauthentication" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationSocialComplete" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationSocialComplete" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationSocialComplete" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -61308,7 +62026,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Count" + "$ref": "#/components/schemas/ReauthenticationResult" } } }, @@ -61323,44 +62041,41 @@ "403": { "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/notifications/makerspace/{makerspace_id}": { - "get": { - "operationId": "api_v1_notifications_makerspace_list", - "summary": "List makerspace notifications", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" + "/api/v1/auth/reauth/social/factor": { + "post": { + "operationId": "api_v1_auth_reauth_social_factor_create", + "tags": [ + "Reauthentication" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationFactor" + } }, - "required": true - }, - { - "in": "query", - "name": "page", - "schema": { - "type": "integer" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationFactor" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationFactor" + } } }, - { - "in": "query", - "name": "unread", - "schema": { - "type": "boolean" - }, - "description": "When true, return only unread notifications." - } - ], - "tags": [ - "Notifications" - ], + "required": true + }, "security": [ { "jwtAuth": [] @@ -61371,49 +62086,56 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedNotificationList" + "$ref": "#/components/schemas/ReauthenticationResult" } } }, "description": "" }, "400": { - "description": "Notifications module is disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." }, "403": { - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/notifications/makerspace/{makerspace_id}/{id}/read": { + "/api/v1/auth/reauth/social/start": { "post": { - "operationId": "api_v1_notifications_makerspace_read_create", - "summary": "Mark a makerspace notification read", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_reauth_social_start_create", "tags": [ - "Notifications" + "Reauthentication" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationSocialStart" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationSocialStart" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ReauthenticationSocialStart" + } + } + }, + "required": true + }, "security": [ { "jwtAuth": [] @@ -61424,205 +62146,144 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Notification" + "$ref": "#/components/schemas/ReauthenticationSocialStarted" } } }, "description": "" }, "400": { - "description": "Notifications module is disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." }, "403": { - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/notifications/makerspace/{makerspace_id}/read-all": { + "/api/v1/auth/refresh": { "post": { - "operationId": "api_v1_notifications_makerspace_read_all_create", - "summary": "Mark all makerspace notifications read", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_refresh_create", + "description": "Takes a refresh type JSON web token and returns an access type JSON web\ntoken if the refresh token is valid.", + "summary": "Refresh access token", "tags": [ - "Notifications" - ], - "security": [ - { - "jwtAuth": [] - } + "Auth" ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotificationMarkAllRead" + "$ref": "#/components/schemas/RefreshResponse" } } }, "description": "" }, - "400": { - "description": "Notifications module is disabled." + "401": { + "description": "Missing, invalid, or replayed refresh token." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "CSRF check failed or account restricted." } } } }, - "/api/v1/notifications/makerspace/{makerspace_id}/unread-count": { - "get": { - "operationId": "api_v1_notifications_makerspace_unread_count_retrieve", - "summary": "Get unread makerspace notification count", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "/api/v1/auth/reset-password": { + "post": { + "operationId": "api_v1_auth_reset_password_create", + "summary": "Confirm an OTP or coexisting legacy password reset link", "tags": [ - "Notifications" + "Auth" ], - "security": [ - { - "jwtAuth": [] + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResetPasswordConfirmRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ResetPasswordConfirmRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ResetPasswordConfirmRequest" + } + } } - ], + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/NotificationUnreadCount" + "$ref": "#/components/schemas/PasswordUpdated" } } }, "description": "" }, "400": { - "description": "Notifications module is disabled." - }, - "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." - } - } - } - }, - "/api/v1/notifications/preferences/{makerspace_id}": { - "get": { - "operationId": "api_v1_notifications_preferences_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Personal notifications" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PreferenceResponse" + "$ref": "#/components/schemas/ResetPasswordConfirmError" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "401": { - "description": "Not signed in." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "429": { + "description": "Request throttled." } } - }, - "patch": { - "operationId": "api_v1_notifications_preferences_partial_update", - "parameters": [ - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + } + }, + "/api/v1/auth/social/apple": { + "post": { + "operationId": "api_v1_auth_social_apple_create", + "description": "Social sign-in with web or attested device delivery. Native Apple audiences bind to the approved iOS app ID on the attestation challenge (or existing grant). New device grants derive staff authority from active local memberships. Superadmin device creation returns device_superadmin_not_supported (403); existing grants remain usable. Attestation outages return attestation_unavailable (503); obtain a new nonce and challenge before retrying.", "tags": [ - "Personal notifications" + "Social auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedPreferenceWrite" + "$ref": "#/components/schemas/SocialLogin" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedPreferenceWrite" + "$ref": "#/components/schemas/SocialLogin" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedPreferenceWrite" + "$ref": "#/components/schemas/SocialLogin" } } - } + }, + "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PreferenceResponse" + "$ref": "#/components/schemas/SocialLoginResponse" } } }, @@ -61640,8 +62301,18 @@ "404": { "description": "Not found, not visible to this actor, or the surface is not enabled here." }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Includes `social_device_restart_required` when burned pre-grant inputs must be replaced." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, "503": { "description": "A required dependency is unavailable." @@ -61649,56 +62320,38 @@ } } }, - "/api/v1/notifications/unsubscribe/{token}": { + "/api/v1/auth/social/github/authorize": { "post": { - "operationId": "api_v1_notifications_unsubscribe_create", - "parameters": [ - { - "in": "path", - "name": "token", - "schema": { - "type": "string" - }, - "required": true - } - ], + "operationId": "api_v1_auth_social_github_authorize_create", "tags": [ - "Personal notifications" + "Social auth" ], "requestBody": { "content": { - "type": { + "application/json": { "schema": { - "type": "object", - "additionalProperties": {}, - "description": "Unspecified request body" + "$ref": "#/components/schemas/GithubBrowserStart" } }, - "required": { + "application/x-www-form-urlencoded": { "schema": { - "type": "object", - "additionalProperties": {}, - "description": "Unspecified request body" + "$ref": "#/components/schemas/GithubBrowserStart" } }, - "properties": { + "multipart/form-data": { "schema": { - "List-Unsubscribe": { - "type": "string", - "enum": [ - "One-Click" - ] - } + "$ref": "#/components/schemas/GithubBrowserStart" } } - } + }, + "required": true }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UnsubscribeResult" + "$ref": "#/components/schemas/GithubBrowserStartResponse" } } }, @@ -61707,14 +62360,11 @@ "400": { "description": "Validation failed, or the makerspace module this route belongs to is off." }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "415": { - "description": "Unsupported media type for the request body." - }, - "423": { - "description": "Makerspace writes are frozen for tenant migration." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, "503": { "description": "A required dependency is unavailable." @@ -61722,79 +62372,115 @@ } } }, - "/api/v1/payment-documents/{document_id}/download": { - "get": { - "operationId": "api_v1_payment_documents_download_retrieve", - "summary": "Download an issued payment document PDF", - "parameters": [ - { - "in": "path", - "name": "document_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "token", - "schema": { - "type": "string" - }, - "required": true - } - ], + "/api/v1/auth/social/github/callback": { + "post": { + "operationId": "api_v1_auth_social_github_callback_create", "tags": [ - "Payments" - ], - "security": [ - { - "jwtAuth": [] - } + "Social auth" ], - "responses": { - "200": { - "content": { - "application/pdf": { - "schema": { - "type": "string", - "format": "binary" - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserCallback" } }, - "description": "" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserCallback" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserCallback" + } + } }, - "400": { + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/OidcBrowserLoginResponse" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "409": { + "description": "Conflicts with the current state." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/auth/social/google": { + "post": { + "operationId": "api_v1_auth_social_google_create", + "description": "Social sign-in with web or attested device delivery. Native Apple audiences bind to the approved iOS app ID on the attestation challenge (or existing grant). New device grants derive staff authority from active local memberships. Superadmin device creation returns device_superadmin_not_supported (403); existing grants remain usable. Attestation outages return attestation_unavailable (503); obtain a new nonce and challenge before retrying.", + "tags": [ + "Social auth" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SocialLogin" } }, - "description": "" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/SocialLogin" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/SocialLogin" + } + } }, - "403": { + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/SocialLoginResponse" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { "content": { "application/json": { "schema": { @@ -61802,274 +62488,190 @@ } } }, - "description": "" - } - } - } - }, - "/api/v1/payments/connect/callback": { - "get": { - "operationId": "api_v1_payments_connect_callback_retrieve", - "summary": "Complete Stripe Connect onboarding", - "parameters": [ - { - "in": "query", - "name": "code", - "schema": { - "type": "string" - } + "description": "Includes `social_device_restart_required` when burned pre-grant inputs must be replaced." }, - { - "in": "query", - "name": "error", - "schema": { - "type": "string" - } + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, - { - "in": "query", - "name": "state", - "schema": { - "type": "string" - }, - "required": true - } - ], - "tags": [ - "Payments" - ], - "responses": { - "302": { - "description": "Redirect to trusted staff settings." + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/procurement/makerspace/{makerspace_id}/to-buy": { - "get": { - "operationId": "api_v1_procurement_makerspace_to_buy_list", - "summary": "List to-buy items for a makerspace", - "parameters": [ - { - "in": "query", - "name": "limit", - "schema": { - "type": "integer" - } - }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "approved", - "cancelled", - "ordered", - "received", - "requested" - ] - }, - "description": "Filter by procurement item status." - } - ], + "/api/v1/auth/social/nonce": { + "post": { + "operationId": "api_v1_auth_social_nonce_create", "tags": [ - "Procurement" - ], - "security": [ - { - "jwtAuth": [] - } + "Social auth" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SocialNonce" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/SocialNonce" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/SocialNonce" + } + } + }, + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ToBuyItem" - } + "$ref": "#/components/schemas/SocialNonceResponse" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - }, - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "403": { - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." } } - }, + } + }, + "/api/v1/auth/social/oidc/{slug}": { "post": { - "operationId": "api_v1_procurement_makerspace_to_buy_create", - "summary": "Add a to-buy item", + "operationId": "api_v1_auth_social_oidc_create", + "description": "Social sign-in with web or attested device delivery. Native Apple audiences bind to the approved iOS app ID on the attestation challenge (or existing grant). New device grants derive staff authority from active local memberships. Superadmin device creation returns device_superadmin_not_supported (403); existing grants remain usable. Attestation outages return attestation_unavailable (503); obtain a new nonce and challenge before retrying.", "parameters": [ - { - "in": "query", - "name": "kind", - "schema": { - "type": "string", - "enum": [ - "hardware", - "printing" - ] - }, - "description": "Stream to add to. Honored only for makerspace admins/superadmin; other roles are auto-tagged by role." - }, { "in": "path", - "name": "makerspace_id", + "name": "slug", "schema": { - "type": "integer" + "type": "string" }, "required": true } ], "tags": [ - "Procurement" + "Social auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyItem" + "$ref": "#/components/schemas/SocialLogin" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/ToBuyItem" + "$ref": "#/components/schemas/SocialLogin" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/ToBuyItem" + "$ref": "#/components/schemas/SocialLogin" } } }, "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyItem" + "$ref": "#/components/schemas/SocialLoginResponse" } } }, "description": "" }, "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." + "description": "Includes `social_device_restart_required` when burned pre-grant inputs must be replaced." }, - "403": { - "description": "Permission denied." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." }, - "404": { - "description": "Not found." + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/procurement/makerspace/{makerspace_id}/to-buy/export": { - "get": { - "operationId": "api_v1_procurement_makerspace_to_buy_export_retrieve", - "summary": "Export to-buy items as CSV or XLSX", + "/api/v1/auth/social/oidc/{slug}/authorize": { + "post": { + "operationId": "api_v1_auth_social_oidc_authorize_create", "parameters": [ - { - "in": "query", - "name": "format", - "schema": { - "type": "string", - "enum": [ - "csv", - "xlsx" - ] - } - }, { "in": "path", - "name": "makerspace_id", + "name": "slug", "schema": { - "type": "integer" + "type": "string" }, "required": true - }, - { - "in": "query", - "name": "status", - "schema": { - "type": "string", - "enum": [ - "approved", - "cancelled", - "ordered", - "received", - "requested" - ] - }, - "description": "Filter by procurement item status." } ], "tags": [ - "Procurement" - ], - "security": [ - { - "jwtAuth": [] - } + "Social auth" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserStart" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserStart" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserStart" + } + } + }, + "required": true + }, "responses": { "200": { "content": { - "text/csv": { - "schema": { - "type": "string" - } - }, - "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/OidcBrowserStartResponse" } } }, @@ -62079,64 +62681,80 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." + "description": "" }, "403": { - "description": "Permission denied." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "404": { - "description": "Not found." - } - } - } - }, - "/api/v1/procurement/makerspace/{makerspace_id}/to-buy/machine-types": { - "get": { - "operationId": "api_v1_procurement_makerspace_to_buy_machine_types_retrieve", - "summary": "List machine types available for a new to-buy item", - "parameters": [ - { - "in": "query", - "name": "kind", - "schema": { - "type": "string", - "enum": [ - "hardware", - "printing" - ] + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "description": "Stream to add to. Honored only for makerspace admins/superadmin; other roles are auto-tagged by role." + "description": "" }, - { - "in": "path", - "name": "makerspace_id", - "schema": { - "type": "integer" + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true + "description": "" + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." } - ], + } + } + }, + "/api/v1/auth/social/oidc/callback": { + "post": { + "operationId": "api_v1_auth_social_oidc_callback_create", "tags": [ - "Procurement" - ], - "security": [ - { - "jwtAuth": [] - } + "Social auth" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserCallback" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserCallback" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/OidcBrowserCallback" + } + } + }, + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyMachineTypeOptions" + "$ref": "#/components/schemas/OidcBrowserLoginResponse" } } }, @@ -62146,40 +62764,63 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." + "description": "" }, "401": { - "description": "Authentication credentials were not provided." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, "403": { - "description": "Permission denied." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" }, - "404": { - "description": "Not found." + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." } } } }, - "/api/v1/procurement/to-buy/{id}": { + "/api/v1/auth/social/providers": { "get": { - "operationId": "api_v1_procurement_to_buy_retrieve", - "summary": "Retrieve a to-buy item", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_social_providers_list", "tags": [ - "Procurement" + "Social auth" ], "security": [ { @@ -62191,67 +62832,47 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyItem" + "type": "array", + "items": { + "$ref": "#/components/schemas/SocialIdentity" + } } } }, "description": "" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - }, - "description": "Invalid request." - }, "401": { - "description": "Authentication credentials were not provided." + "description": "Not signed in." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." } } }, - "patch": { - "operationId": "api_v1_procurement_to_buy_partial_update", - "summary": "Update a to-buy item", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "post": { + "operationId": "api_v1_auth_social_providers_create", "tags": [ - "Procurement" + "Social auth" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PatchedToBuyItem" + "$ref": "#/components/schemas/SocialLink" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PatchedToBuyItem" + "$ref": "#/components/schemas/SocialLink" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PatchedToBuyItem" + "$ref": "#/components/schemas/SocialLink" } } - } + }, + "required": true }, "security": [ { @@ -62263,48 +62884,48 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyItem" + "$ref": "#/components/schemas/SocialIdentity" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - }, - "description": "Invalid request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "description": "Authentication credentials were not provided." + "description": "Not signed in." }, "403": { - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." + }, + "503": { + "description": "A required dependency is unavailable." } } - }, + } + }, + "/api/v1/auth/social/providers/{provider}": { "delete": { - "operationId": "api_v1_procurement_to_buy_destroy", - "summary": "Delete a to-buy item", + "operationId": "api_v1_auth_social_providers_destroy", "parameters": [ { "in": "path", - "name": "id", + "name": "provider", "schema": { - "type": "integer" + "type": "string" }, "required": true } ], "tags": [ - "Procurement" + "Social auth" ], "security": [ { @@ -62315,198 +62936,177 @@ "204": { "description": "No response body" }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - }, - "description": "Invalid request." - }, "401": { - "description": "Authentication credentials were not provided." + "description": "Not signed in." }, "403": { - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "description": "Not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." } } } }, - "/api/v1/procurement/to-buy/{id}/move-to-inventory": { + "/api/v1/auth/staff-invitations/accept": { "post": { - "operationId": "api_v1_procurement_to_buy_move_to_inventory_create", - "summary": "Move a received hardware to-buy item into inventory", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], + "operationId": "api_v1_auth_staff_invitations_accept_create", "tags": [ - "Procurement" + "Staff invitations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MoveToInventoryRequest" + "$ref": "#/components/schemas/StaffInvitationAccept" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MoveToInventoryRequest" + "$ref": "#/components/schemas/StaffInvitationAccept" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MoveToInventoryRequest" + "$ref": "#/components/schemas/StaffInvitationAccept" } } }, "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyItem" + "$ref": "#/components/schemas/StaffInvitationAccepted" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - }, - "description": "Invalid request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "description": "Authentication credentials were not provided." + "description": "Not signed in." }, "403": { - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Not found." + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/procurement/to-buy/{id}/move-to-printing": { - "post": { - "operationId": "api_v1_procurement_to_buy_move_to_printing_create", - "summary": "Move a received printing to-buy item into printing assets", + "/api/v1/backups/download/{archive_id}/{token}": { + "get": { + "operationId": "api_v1_backups_download_retrieve", + "summary": "Consume a one-use backup download", "parameters": [ { "in": "path", - "name": "id", + "name": "archive_id", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true - } - ], - "tags": [ - "Procurement" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MoveToPrintingRequest" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/MoveToPrintingRequest" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/MoveToPrintingRequest" - } - } }, - "required": true - }, - "security": [ { - "jwtAuth": [] + "in": "path", + "name": "token", + "schema": { + "type": "string" + }, + "required": true } ], + "tags": [ + "Backup" + ], "responses": { "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ToBuyItem" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - }, - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." + "description": "Age-encrypted archive stream." }, "404": { - "description": "Not found." + "description": "Invalid or expired download." } } } }, - "/api/v1/procurement/to-buy/{id}/receipts": { + "/api/v1/booking-integration/bookings/": { "get": { - "operationId": "api_v1_procurement_to_buy_receipts_list", - "summary": "List procurement receipts for a to-buy item", + "operationId": "api_v1_booking_integration_bookings_retrieve", + "description": "Requires bookings:read; bookings:attendance:read adds current unreversed arrived_at, including arrivals retained after completion. No contacts, answers, notes, credentials or payment data. GET and HEAD audit the disclosure under the tenant source fence.", + "summary": "Read minimal tenant reservations (server HMAC; bookings:read)", "parameters": [ { - "in": "path", - "name": "id", + "in": "header", + "name": "X-Client-Id", "schema": { - "type": "integer" + "type": "string" + }, + "required": true + }, + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Required when nonce enforcement is enabled." + }, + { + "in": "header", + "name": "X-Signature", + "schema": { + "type": "string" + }, + "description": "HMAC-SHA256 of method, full path, timestamp, optional nonce and raw body.", + "required": true + }, + { + "in": "header", + "name": "X-Timestamp", + "schema": { + "type": "string" }, "required": true + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + }, + "description": "Opaque, signed continuation cursor." + }, + { + "in": "query", + "name": "page_size", + "schema": { + "type": "integer" + }, + "description": "1–200; default 100." } ], "tags": [ - "Procurement" + "Server bookings" ], "security": [ { - "jwtAuth": [] + "ServerHMAC": [] } ], "responses": { @@ -62514,10 +63114,7 @@ "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ToBuyReceipt" - } + "$ref": "#/components/schemas/IntegrationBookingPage" } } }, @@ -62527,105 +63124,114 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." + "description": "Invalid cursor or page size." }, "401": { - "description": "Authentication credentials were not provided." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Missing or invalid HMAC signature." }, "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." - } - } - }, - "post": { - "operationId": "api_v1_procurement_to_buy_receipts_create", - "summary": "Finalize an uploaded procurement receipt", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Procurement" - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ToBuyReceiptFinalize" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ToBuyReceiptFinalize" + "description": "Server type, tenant binding or required scopes missing." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } } }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ToBuyReceiptFinalize" - } - } + "description": "Tenant/subject/module/feature unavailable." }, - "required": true - }, - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "201": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyReceipt" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Read rate exceeded." }, - "400": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." + "description": "Source tenant is frozen; audited reads cannot append history." }, "503": { - "description": "Receipt storage is unavailable." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source gate could not be proven available." } } } }, - "/api/v1/procurement/to-buy/{id}/receipts/presign": { - "post": { - "operationId": "api_v1_procurement_to_buy_receipts_presign_create", - "summary": "Create a procurement receipt upload URL", + "/api/v1/booking-integration/bookings/{id}/": { + "get": { + "operationId": "api_v1_booking_integration_bookings_retrieve_2", + "description": "Requires bookings:read; bookings:attendance:read adds current unreversed arrived_at, including arrivals retained after completion. No contacts, answers, notes, credentials or payment data. GET and HEAD audit the disclosure under the tenant source fence.", + "summary": "Read one tenant reservation (server HMAC; bookings:read)", "parameters": [ + { + "in": "header", + "name": "X-Client-Id", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Required when nonce enforcement is enabled." + }, + { + "in": "header", + "name": "X-Signature", + "schema": { + "type": "string" + }, + "description": "HMAC-SHA256 of method, full path, timestamp, optional nonce and raw body.", + "required": true + }, + { + "in": "header", + "name": "X-Timestamp", + "schema": { + "type": "string" + }, + "required": true + }, { "in": "path", "name": "id", @@ -62636,39 +63242,19 @@ } ], "tags": [ - "Procurement" + "Server bookings" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ToBuyReceiptPresign" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/ToBuyReceiptPresign" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/ToBuyReceiptPresign" - } - } - }, - "required": true - }, "security": [ { - "jwtAuth": [] + "ServerHMAC": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyReceiptUploadResponse" + "$ref": "#/components/schemas/IntegrationBooking" } } }, @@ -62678,220 +63264,150 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." - }, - "503": { - "description": "Receipt storage is unavailable." + "description": "Invalid cursor or page size." }, "401": { - "description": "Authentication credentials were not provided." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Missing or invalid HMAC signature." }, "403": { - "description": "Permission denied." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Server type, tenant binding or required scopes missing." }, "404": { - "description": "Not found." - } - } - } - }, - "/api/v1/procurement/to-buy/receipts/{id}": { - "delete": { - "operationId": "api_v1_procurement_to_buy_receipts_destroy", - "summary": "Delete a procurement receipt", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Procurement" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "204": { - "description": "No response body" + "description": "Tenant/subject/module/feature unavailable." }, - "400": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." + "description": "Read rate exceeded." }, - "404": { - "description": "Not found." - } - } - } - }, - "/api/v1/procurement/to-buy/receipts/{id}/url": { - "get": { - "operationId": "api_v1_procurement_to_buy_receipts_url_retrieve", - "summary": "Create a signed procurement receipt view URL", - "parameters": [ - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" - }, - "required": true - } - ], - "tags": [ - "Procurement" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ToBuyReceiptUrl" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Source tenant is frozen; audited reads cannot append history." }, "503": { - "description": "Receipt storage is unavailable." - }, - "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." - }, - "401": { - "description": "Authentication credentials were not provided." - }, - "403": { - "description": "Permission denied." - }, - "404": { - "description": "Not found." + "description": "Source gate could not be proven available." } } } }, - "/api/v1/public/{makerspace_slug}/bookings/calendar/{raw_token}.ics": { + "/api/v1/booking-integration/resources/": { "get": { - "operationId": "api_v1_public_bookings_calendar_.ics_retrieve", + "operationId": "api_v1_booking_integration_resources_retrieve", + "summary": "Read tenant resources (server HMAC; booking_resources:read)", "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "header", + "name": "X-Client-Id", "schema": { "type": "string" }, "required": true }, { - "in": "path", - "name": "raw_token", + "in": "header", + "name": "X-Nonce", "schema": { "type": "string" }, - "required": true - } - ], - "tags": [ - "Member bookings" - ], - "responses": { - "200": { - "content": { - "text/calendar": { - "schema": { - "type": "string" - } - } - }, - "description": "Own reservations as RFC 5545 ICS." - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, - "403": { - "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." - }, - "404": { - "description": "Not found, not visible to this actor, or the surface is not enabled here." + "description": "Required when nonce enforcement is enabled." }, - "429": { - "description": "Rate limit exceeded; retry after the interval in Retry-After." - } - } - } - }, - "/api/v1/public/{makerspace_slug}/event-calendar/{raw_token}.ics": { - "get": { - "operationId": "api_v1_public_event_calendar_.ics_retrieve", - "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "header", + "name": "X-Signature", "schema": { "type": "string" }, + "description": "HMAC-SHA256 of method, full path, timestamp, optional nonce and raw body.", "required": true }, { - "in": "path", - "name": "raw_token", + "in": "header", + "name": "X-Timestamp", "schema": { "type": "string" }, "required": true + }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + }, + "description": "Opaque, signed continuation cursor." + }, + { + "in": "query", + "name": "page_size", + "schema": { + "type": "integer" + }, + "description": "1–200; default 100." } ], "tags": [ - "Public events" + "Server bookings" + ], + "security": [ + { + "ServerHMAC": [] + } ], "responses": { "200": { "content": { - "text/calendar": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/IntegrationBookingResourcePage" } } }, - "description": "RFC 5545 calendar (`text/calendar; charset=utf-8`)." + "description": "" }, - "404": { + "400": { "content": { "application/json": { "schema": { @@ -62899,9 +63415,9 @@ } } }, - "description": "Calendar not found." + "description": "Invalid cursor or page size." }, - "429": { + "401": { "content": { "application/json": { "schema": { @@ -62909,106 +63425,94 @@ } } }, - "description": "Rate limit exceeded." - } - } - } - }, - "/api/v1/public/{makerspace_slug}/events/": { - "get": { - "operationId": "api_v1_public_events_list", - "parameters": [ - { - "in": "query", - "name": "category", - "schema": { - "type": "string" - } - }, - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" - }, - "required": true + "description": "Missing or invalid HMAC signature." }, - { - "in": "query", - "name": "q", - "schema": { - "type": "string" + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "description": "Full-text search over title, location and description; supports \"phrases\" and -exclusions, tolerates typos in the title." + "description": "Server type, tenant binding or required scopes missing." }, - { - "in": "query", - "name": "tag", - "schema": { - "type": "string" - } - } - ], - "tags": [ - "Public events" - ], - "responses": { - "200": { + "404": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicEvent" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Tenant/subject/module/feature unavailable." }, - "400": { + "429": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." + "description": "Read rate exceeded." }, - "404": { + "423": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Event not found." + "description": "Source tenant is frozen; audited reads cannot append history." }, - "429": { + "503": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Rate limit exceeded." + "description": "Source gate could not be proven available." } } } }, - "/api/v1/public/{makerspace_slug}/events/{public_token}/": { + "/api/v1/booking-integration/resources/{id}/": { "get": { - "operationId": "api_v1_public_events_retrieve", + "operationId": "api_v1_booking_integration_resources_retrieve_2", + "summary": "Read one tenant resource (server HMAC; booking_resources:read)", "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "header", + "name": "X-Client-Id", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Required when nonce enforcement is enabled." + }, + { + "in": "header", + "name": "X-Signature", + "schema": { + "type": "string" + }, + "description": "HMAC-SHA256 of method, full path, timestamp, optional nonce and raw body.", + "required": true + }, + { + "in": "header", + "name": "X-Timestamp", "schema": { "type": "string" }, @@ -63016,23 +63520,27 @@ }, { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Public events" + "Server bookings" + ], + "security": [ + { + "ServerHMAC": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicEvent" + "$ref": "#/components/schemas/IntegrationBookingResource" } } }, @@ -63042,76 +63550,43 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid request." + "description": "Invalid cursor or page size." }, - "404": { + "401": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Event not found." + "description": "Missing or invalid HMAC signature." }, - "429": { + "403": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Rate limit exceeded." - } - } - } - }, - "/api/v1/public/{makerspace_slug}/events/{public_token}/calendar.ics": { - "get": { - "operationId": "api_v1_public_events_calendar.ics_retrieve", - "parameters": [ - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" - }, - "required": true + "description": "Server type, tenant binding or required scopes missing." }, - { - "in": "path", - "name": "public_token", - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true - } - ], - "tags": [ - "Public events" - ], - "responses": { - "200": { + "404": { "content": { - "text/calendar": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "RFC 5545 calendar (`text/calendar; charset=utf-8`)." + "description": "Tenant/subject/module/feature unavailable." }, - "400": { + "429": { "content": { "application/json": { "schema": { @@ -63119,9 +63594,9 @@ } } }, - "description": "Events module unavailable." + "description": "Read rate exceeded." }, - "404": { + "423": { "content": { "application/json": { "schema": { @@ -63129,9 +63604,9 @@ } } }, - "description": "Calendar not found." + "description": "Source tenant is frozen; audited reads cannot append history." }, - "429": { + "503": { "content": { "application/json": { "schema": { @@ -63139,42 +63614,89 @@ } } }, - "description": "Rate limit exceeded." + "description": "Source gate could not be proven available." } } } }, - "/api/v1/public/{makerspace_slug}/events/{public_token}/feedback/": { + "/api/v1/booking-integration/resources/{id}/bookings/": { "get": { - "operationId": "api_v1_public_events_feedback_retrieve", + "operationId": "api_v1_booking_integration_resources_bookings_retrieve", + "description": "Requires bookings:read; bookings:attendance:read adds current unreversed arrived_at, including arrivals retained after completion. No contacts, answers, notes, credentials or payment data. GET and HEAD audit the disclosure under the tenant source fence.", + "summary": "Read reservations for one resource (server HMAC; bookings:read)", "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "header", + "name": "X-Client-Id", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Required when nonce enforcement is enabled." + }, + { + "in": "header", + "name": "X-Signature", + "schema": { + "type": "string" + }, + "description": "HMAC-SHA256 of method, full path, timestamp, optional nonce and raw body.", + "required": true + }, + { + "in": "header", + "name": "X-Timestamp", "schema": { "type": "string" }, "required": true }, + { + "in": "query", + "name": "cursor", + "schema": { + "type": "string" + }, + "description": "Opaque, signed continuation cursor." + }, { "in": "path", - "name": "public_token", + "name": "id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true + }, + { + "in": "query", + "name": "page_size", + "schema": { + "type": "integer" + }, + "description": "1–200; default 100." } ], "tags": [ - "Public events" + "Server bookings" + ], + "security": [ + { + "ServerHMAC": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedbackForm" + "$ref": "#/components/schemas/IntegrationBookingPage" } } }, @@ -63188,7 +63710,7 @@ } } }, - "description": "Invalid feedback answers." + "description": "Invalid cursor or page size." }, "401": { "content": { @@ -63198,9 +63720,9 @@ } } }, - "description": "Authentication is required for a certificate." + "description": "Missing or invalid HMAC signature." }, - "404": { + "403": { "content": { "application/json": { "schema": { @@ -63208,9 +63730,9 @@ } } }, - "description": "Feedback form not found." + "description": "Server type, tenant binding or required scopes missing." }, - "409": { + "404": { "content": { "application/json": { "schema": { @@ -63218,7 +63740,7 @@ } } }, - "description": "Feedback retry conflict." + "description": "Tenant/subject/module/feature unavailable." }, "429": { "content": { @@ -63228,53 +63750,54 @@ } } }, - "description": "Rate limit exceeded." + "description": "Read rate exceeded." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source tenant is frozen; audited reads cannot append history." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source gate could not be proven available." } } - }, - "post": { - "operationId": "api_v1_public_events_feedback_create", + } + }, + "/api/v1/bootstrap": { + "get": { + "operationId": "api_v1_bootstrap_retrieve", + "summary": "Resolve tenant and frontend-safe configuration", "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "query", + "name": "slug", "schema": { "type": "string" - }, - "required": true + } }, { - "in": "path", - "name": "public_token", + "in": "query", + "name": "tenant", "schema": { - "type": "string", - "format": "uuid" - }, - "required": true + "type": "string" + } } ], "tags": [ - "Public events" + "Tenant bootstrap" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FeedbackSubmission" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/FeedbackSubmission" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/FeedbackSubmission" - } - } - } - }, "security": [ { "jwtAuth": [] @@ -63282,84 +63805,113 @@ {} ], "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FeedbackSubmissionResponse" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid feedback answers." - }, - "401": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/TenantBootstrap" } } }, - "description": "Authentication is required for a certificate." + "description": "Frontend-safe tenant bootstrap payload." }, "404": { + "description": "No active tenant frontend matched." + } + } + } + }, + "/api/v1/config": { + "get": { + "operationId": "api_v1_config_retrieve", + "description": "Includes native minimum app versions and public provider settings only while the mobile module permits new grants.", + "summary": "Return frontend-safe platform configuration", + "tags": [ + "Platform" + ], + "security": [ + {} + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PublicConfig" } } }, - "description": "Feedback form not found." + "description": "" }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Feedback retry conflict." + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/public/{makerspace_slug}/events/{public_token}/register/": { - "post": { - "operationId": "api_v1_public_events_register_create", + "/api/v1/data-exports/download/{job_id}/{token}": { + "get": { + "operationId": "api_v1_data_exports_download_retrieve", + "summary": "Consume a one-use export download URL", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "token", "schema": { "type": "string" }, "required": true + } + ], + "tags": [ + "Data exports" + ], + "responses": { + "200": { + "content": { + "application/zip": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "404": { + "description": "Download link is invalid, expired, or used." }, + "503": { + "description": "Archive storage is unavailable." + } + } + } + }, + "/api/v1/device/commands/{id}/ack": { + "post": { + "operationId": "api_v1_device_commands_ack_create", + "parameters": [ { "in": "path", - "name": "public_token", + "name": "id", "schema": { "type": "string", "format": "uuid" @@ -63368,38 +63920,39 @@ } ], "tags": [ - "Public events" + "Device protocol" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicEventRegistrationInput" + "$ref": "#/components/schemas/Ack" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicEventRegistrationInput" + "$ref": "#/components/schemas/Ack" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicEventRegistrationInput" + "$ref": "#/components/schemas/Ack" } } - } + }, + "required": true }, "security": [ { - "jwtAuth": [] + "DeviceSignature": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicEventRegistrationResponse" + "$ref": "#/components/schemas/CommandAck" } } }, @@ -63409,153 +63962,92 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid request." + "description": "Invalid payload or adapter module disabled." }, - "404": { + "401": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event not found." + "description": "Authentication/signature/replay refused." }, - "429": { + "403": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Rate limit exceeded." + "description": "Authority or device revoked." }, - "401": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Authentication is required." + "description": "Target outside scope or absent." }, - "403": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "An active authenticated account is required; member audiences also require active membership and the current waiver." + "description": "Conflicting command/report." }, - "409": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Event state conflict or cancelled paid registration cannot be reused." + "description": "Delivery lease expired." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/public/{makerspace_slug}/inventory/": { - "get": { - "operationId": "api_v1_public_inventory_list", - "description": "List public inventory products for a public makerspace.", - "summary": "List public inventory products", - "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." - }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" - }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." - }, - { - "in": "query", - "name": "category", - "schema": { - "type": "string" - }, - "description": "Filter public products by category slug." - }, - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" - }, - "description": "Public makerspace code (for example TSEL) or slug.", - "required": true - }, - { - "name": "page", - "required": false, - "in": "query", - "description": "A page number within the paginated result set.", - "schema": { - "type": "integer" - } - }, - { - "in": "query", - "name": "q", - "schema": { - "type": "string" - }, - "description": "Full-text search over public name and description; supports \"phrases\" and -exclusions, tolerates typos in the name." - }, - { - "in": "query", - "name": "sort", - "schema": { - "type": "string", - "enum": [ - "most_used", - "name", - "popular" - ] - }, - "description": "Sort public products." - } - ], + "/api/v1/device/commands/lease": { + "post": { + "operationId": "api_v1_device_commands_lease_create", "tags": [ - "Public inventory" + "Device protocol" ], "security": [ { - "jwtAuth": [] - }, - {} + "DeviceSignature": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedPublicProductList" + "$ref": "#/components/schemas/LeaseResponse" } } }, @@ -63565,109 +64057,119 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Public inventory module or loans feature is disabled." + "description": "Invalid payload or adapter module disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid authentication credentials." + "description": "Authentication/signature/replay refused." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Public API client is not authorized." + "description": "Authority or device revoked." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Item or public makerspace not found, or hidden by edition." + "description": "Target outside scope or absent." }, - "429": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Public read rate limit exceeded." + "description": "Conflicting command/report." + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Delivery lease expired." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/public/{makerspace_slug}/inventory/{id}/": { - "get": { - "operationId": "api_v1_public_inventory_retrieve", - "description": "Read-only public catalogue detail. Private, archived and cross-space items share the same not-found response.", - "summary": "Get public inventory product detail", + "/api/v1/device/enrollments/{id}/challenge": { + "post": { + "operationId": "api_v1_device_enrollments_challenge_create", + "summary": "Request a proof-of-possession challenge for a candidate key", "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." - }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" - }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." - }, { "in": "path", "name": "id", "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Public inventory" + "Device protocol" ], - "security": [ - { - "jwtAuth": [] + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChallengeRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ChallengeRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ChallengeRequest" + } + } }, - {} - ], + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicProduct" + "$ref": "#/components/schemas/ChallengeResponse" } } }, @@ -63677,166 +64179,215 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Public inventory module or loans feature is disabled." + "description": "Malformed key, challenge or signature, or module disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid authentication credentials." + "description": "Not authenticated." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Public API client is not authorized." + "description": "Enrollment credentials or proof of possession rejected." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Item or public makerspace not found, or hidden by edition." + "description": "Not found." }, - "429": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Public read rate limit exceeded." + "description": "The key is already bound to another device, or rotation reused the key." + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "The enrollment was used, revoked or has expired." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/public/{makerspace_slug}/inventory/{id}/watch/": { + "/api/v1/device/enrollments/{id}/complete": { "post": { - "operationId": "api_v1_public_inventory_watch_create", + "operationId": "api_v1_device_enrollments_complete_create", + "summary": "Complete enrollment by signing the challenge with the candidate key", "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." - }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" - }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." - }, { "in": "path", "name": "id", "schema": { - "type": "integer" - }, - "required": true - }, - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "api" - ], - "security": [ - { - "jwtAuth": [] - } + "Device protocol" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CompleteRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CompleteRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CompleteRequest" + } + } + }, + "required": true + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WatchResponse" + "$ref": "#/components/schemas/ConnectedDeviceIdentity" } } }, "description": "" }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Malformed key, challenge or signature, or module disabled." + }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Not authenticated." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Enrollment credentials or proof of possession rejected." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Not found." }, - "423": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "The key is already bound to another device, or rotation reused the key." }, - "429": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "The enrollment was used, revoked or has expired." }, - "503": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." + } + } + } + }, + "/api/v1/device/heartbeat": { + "post": { + "operationId": "api_v1_device_heartbeat_create", + "summary": "Report device activity and recover an offline device (signed request)", + "tags": [ + "Device protocol" + ], + "security": [ + { + "DeviceSignature": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConnectedDeviceIdentity" } } }, @@ -63846,185 +64397,213 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" - } - } - }, - "delete": { - "operationId": "api_v1_public_inventory_watch_destroy", - "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "description": "Module disabled." }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "description": "Missing, malformed, stale, replayed or invalid signature." }, - { - "in": "path", - "name": "id", - "schema": { - "type": "integer" + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } }, - "required": true + "description": "Device not allowed on this route, revoked or tenant unavailable." }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." + } + } + } + }, + "/api/v1/device/label-commands/{id}/content": { + "get": { + "operationId": "api_v1_device_label_commands_content_retrieve", + "summary": "Download acknowledged label ZIP", + "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "id", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "api" + "Device protocol" ], "security": [ { - "jwtAuth": [] + "DeviceSignature": [] } ], "responses": { - "204": { - "description": "No response body" + "200": { + "content": { + "application/zip": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] } } }, - "description": "" + "description": "DRF field/module validation errors or non-field validation messages." }, - "403": { + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Authentication required or invalid signature." }, - "404": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Permission denied." }, - "423": { + "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Command or batch not in scope." }, - "429": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Idempotency or delivery conflict." }, - "503": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Batch no longer available." }, - "400": { + "423": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Tenant frozen for migration." } } } }, - "/api/v1/public/{makerspace_slug}/inventory/categories/": { - "get": { - "operationId": "api_v1_public_inventory_categories_list", - "summary": "List public inventory categories", + "/api/v1/device/label-commands/{id}/result": { + "post": { + "operationId": "api_v1_device_label_commands_result_create", + "summary": "Report an idempotent label result", "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." - }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" - }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." - }, { "in": "path", - "name": "makerspace_slug", + "name": "id", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Public inventory" + "Device protocol" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LabelResult" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/LabelResult" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/LabelResult" + } + } + }, + "required": true + }, "security": [ { - "jwtAuth": [] - }, - {} + "DeviceSignature": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicCategory" - } + "$ref": "#/components/schemas/LabelReceipt" } } }, @@ -64034,87 +64613,118 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "oneOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] } } }, - "description": "Public inventory module or loans feature is disabled." + "description": "DRF field/module validation errors or non-field validation messages." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid authentication credentials." + "description": "Authentication required or invalid signature." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Public API client is not authorized." + "description": "Permission denied." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Item or public makerspace not found, or hidden by edition." + "description": "Command or batch not in scope." }, - "429": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Public read rate limit exceeded." + "description": "Idempotency or delivery conflict." + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Batch no longer available." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/public/{makerspace_slug}/invitation-requests": { + "/api/v1/device/printer-jobs/{id}/result": { "post": { - "operationId": "api_v1_public_invitation_requests_create", - "summary": "Ask a makerspace to invite you", + "operationId": "api_v1_device_printer_jobs_result_create", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "id", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Memberships" + "Device protocol" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InvitationRequestCreate" + "$ref": "#/components/schemas/Result" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/InvitationRequestCreate" + "$ref": "#/components/schemas/Result" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/InvitationRequestCreate" + "$ref": "#/components/schemas/Result" } } }, @@ -64122,16 +64732,15 @@ }, "security": [ { - "jwtAuth": [] - }, - {} + "DeviceSignature": [] + } ], "responses": { - "202": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/InvitationRequestAck" + "$ref": "#/components/schemas/Job" } } }, @@ -64141,80 +64750,107 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Invalid payload or adapter module disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authentication/signature/replay refused." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Authority or device revoked." }, "404": { - "description": "Unknown makerspace." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Target outside scope or absent." }, - "423": { + "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Conflicting command/report." }, - "429": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Delivery lease expired." }, - "503": { + "423": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "" + "description": "Tenant frozen for migration." } } } }, - "/api/v1/public/{makerspace_slug}/machine-service-requests": { + "/api/v1/device/printer-jobs/{id}/telemetry": { "post": { - "operationId": "api_v1_public_machine_service_requests_create", - "summary": "Submit a machine service request as a member", + "operationId": "api_v1_device_printer_jobs_telemetry_create", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "id", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Public machine service" + "Device protocol" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicMachineServiceSubmit" + "$ref": "#/components/schemas/Telemetry" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicMachineServiceSubmit" + "$ref": "#/components/schemas/Telemetry" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicMachineServiceSubmit" + "$ref": "#/components/schemas/Telemetry" } } }, @@ -64222,15 +64858,15 @@ }, "security": [ { - "jwtAuth": [] + "DeviceSignature": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicMachineServiceSubmitResponse" + "$ref": "#/components/schemas/Job" } } }, @@ -64240,293 +64876,354 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Invalid machine service request input." + "description": "Invalid payload or adapter module disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Authentication is required." + "description": "Authentication/signature/replay refused." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Active membership, waiver acceptance, and presence are required." + "description": "Authority or device revoked." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Makerspace or machine not found." + "description": "Target outside scope or absent." }, "409": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Machine service request conflict." + "description": "Conflicting command/report." }, - "429": { + "410": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/DeviceError" } } }, - "description": "Request rate limit exceeded." + "description": "Delivery lease expired." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeviceError" + } + } + }, + "description": "Tenant frozen for migration." } } } }, - "/api/v1/public/{makerspace_slug}/machine-service/3d-printer/consumable-pools": { + "/api/v1/discover/{kind}/": { "get": { - "operationId": "api_v1_public_machine_service_3d_printer_consumable_pools_list", + "operationId": "api_v1_discover_retrieve", "parameters": [ + { + "in": "query", + "name": "date_from", + "schema": { + "type": "string", + "format": "date" + } + }, + { + "in": "query", + "name": "date_to", + "schema": { + "type": "string", + "format": "date" + } + }, { "in": "path", - "name": "makerspace_slug", + "name": "kind", "schema": { "type": "string" }, "required": true + }, + { + "in": "query", + "name": "kind", + "schema": { + "type": "string" + }, + "description": "Booking resource kind (query filter); events use date filters instead." + }, + { + "in": "query", + "name": "location", + "schema": { + "type": "string" + }, + "description": "Booking resource location contains this text." + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string", + "maxLength": 200 + } } ], "tags": [ - "Public machine service" + "Public websites" ], "responses": { "200": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicPrinterPool" - } + "$ref": "#/components/schemas/Discovery" } } }, "description": "" - } - } - } - }, - "/api/v1/public/{makerspace_slug}/machine-service/3d-printer/queues": { - "get": { - "operationId": "api_v1_public_machine_service_3d_printer_queues_list", - "parameters": [ - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - } - ], - "tags": [ - "Public machine service" - ], - "responses": { - "200": { + "description": "Invalid discovery filters." + }, + "404": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicPrinterQueue" - } + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Unknown or unavailable public content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." } } } }, - "/api/v1/public/{makerspace_slug}/machine-service/3d-printer/requests": { - "post": { - "operationId": "api_v1_public_machine_service_3d_printer_requests_create", + "/api/v1/event-checkin-stations/{public_token}/roster/": { + "get": { + "operationId": "api_v1_event_checkin_stations_roster_retrieve", + "summary": "Download the station's minimal expiring attendee roster", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "public_token", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Public machine service" + "Event check-in stations" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicPrinterSubmit" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PublicPrinterSubmit" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PublicPrinterSubmit" - } - } - }, - "required": true - }, "security": [ { - "jwtAuth": [] - } + "EventStationCookie": [] + }, + {} ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicPrinterSubmitResponse" + "$ref": "#/components/schemas/OfflineRosterResponse" } } }, "description": "" - } - } - } - }, - "/api/v1/public/{makerspace_slug}/machine-service/3d-printer/uploads": { - "post": { - "operationId": "api_v1_public_machine_service_3d_printer_uploads_create", + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid station credential, session, origin, feature, or time window." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid station credential, session, origin, feature, or time window." + }, + "413": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid station credential, session, origin, feature, or time window." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid station credential, session, origin, feature, or time window." + } + } + } + }, + "/api/v1/event-checkin-stations/{public_token}/session/": { + "post": { + "operationId": "api_v1_event_checkin_stations_session_create", + "summary": "Exchange an event-scoped PIN for a station session", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "public_token", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Public machine service" + "Event check-in stations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicPrinterUpload" + "$ref": "#/components/schemas/StationPin" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicPrinterUpload" + "$ref": "#/components/schemas/StationPin" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicPrinterUpload" + "$ref": "#/components/schemas/StationPin" } } }, "required": true }, - "security": [ - { - "jwtAuth": [] - } - ], "responses": { - "201": { + "204": { + "description": "No response body" + }, + "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicPrinterSubmitResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - } - } - } - }, - "/api/v1/public/{makerspace_slug}/machine-types/{type_id}/intake": { - "get": { - "operationId": "api_v1_public_machine_types_intake_retrieve", - "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "description": "Invalid station credential, session, origin, feature, or time window." }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "description": "Invalid station credential, session, origin, feature, or time window." }, - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } }, - "required": true - }, + "description": "Invalid station credential, session, origin, feature, or time window." + } + } + }, + "delete": { + "operationId": "api_v1_event_checkin_stations_session_destroy", + "summary": "Clear the local station session cookie", + "parameters": [ { "in": "path", - "name": "type_id", + "name": "public_token", "schema": { - "type": "integer" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "api" + "Event check-in stations" + ], + "security": [ + { + "EventStationCookie": [] + } ], "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicTypeIntake" - } - } - }, - "description": "" + "204": { + "description": "No response body" }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -64534,9 +65231,9 @@ } } }, - "description": "" + "description": "Invalid station credential, session, origin, feature, or time window." }, - "403": { + "429": { "content": { "application/json": { "schema": { @@ -64544,19 +65241,67 @@ } } }, - "description": "" + "description": "Invalid station credential, session, origin, feature, or time window." + } + } + } + }, + "/api/v1/event-checkin-stations/{public_token}/sync/": { + "post": { + "operationId": "api_v1_event_checkin_stations_sync_create", + "summary": "Synchronize queued PIN-station check-ins", + "parameters": [ + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Event check-in stations" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/OfflineCheckInSyncRequest" + } + } }, - "404": { + "required": true + }, + "security": [ + { + "EventStationCookie": [] + }, + {} + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/OfflineCheckInSyncResponse" } } }, "description": "" }, - "423": { + "400": { "content": { "application/json": { "schema": { @@ -64564,9 +65309,9 @@ } } }, - "description": "" + "description": "Invalid station credential, session, origin, feature, or time window." }, - "429": { + "403": { "content": { "application/json": { "schema": { @@ -64574,9 +65319,9 @@ } } }, - "description": "" + "description": "Invalid station credential, session, origin, feature, or time window." }, - "503": { + "410": { "content": { "application/json": { "schema": { @@ -64584,86 +65329,81 @@ } } }, - "description": "" + "description": "Invalid station credential, session, origin, feature, or time window." }, - "400": { + "429": { "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" + "description": "Invalid station credential, session, origin, feature, or time window." } } } }, - "/api/v1/public/{makerspace_slug}/machines": { + "/api/v1/events/": { "get": { - "operationId": "api_v1_public_machines_list", - "description": "List active machines published by a public makerspace.", - "summary": "List public machines", + "operationId": "api_v1_events_retrieve", + "summary": "Read minimal tenant events (server HMAC; events:read)", "parameters": [ { "in": "header", - "name": "X-Nonce", + "name": "X-Client-Id", "schema": { "type": "string" }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "required": true }, { "in": "header", - "name": "X-Publishable-Key", + "name": "X-Nonce", "schema": { "type": "string" }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "description": "Required when nonce enforcement is enabled." }, { - "in": "path", - "name": "makerspace_slug", + "in": "header", + "name": "X-Timestamp", "schema": { "type": "string" }, - "description": "Public makerspace code or slug.", "required": true }, { - "name": "page", - "required": false, "in": "query", - "description": "A page number within the paginated result set.", + "name": "cursor", "schema": { - "type": "integer" - } + "type": "string" + }, + "description": "Opaque, signed continuation cursor." }, { "in": "query", - "name": "q", + "name": "page_size", "schema": { - "type": "string" + "type": "integer" }, - "description": "Full-text search over name, location, firmware version and notes; tolerates typos in the name." + "description": "1–200; default 100." } ], "tags": [ - "Public machines" + "Server events" ], "security": [ { - "jwtAuth": [] - }, - {} + "ServerHMAC": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedPublicMachineList" + "$ref": "#/components/schemas/IntegrationEventPage" } } }, @@ -64673,21 +65413,13 @@ "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "module": { - "type": "string" - } - }, - "required": [ - "module" - ] + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Machines module disabled." + "description": "Invalid cursor or page size." }, - "404": { + "401": { "content": { "application/json": { "schema": { @@ -64695,9 +65427,9 @@ } } }, - "description": "Public makerspace not found." + "description": "Missing or invalid HMAC signature." }, - "401": { + "403": { "content": { "application/json": { "schema": { @@ -64705,9 +65437,9 @@ } } }, - "description": "Invalid API credentials." + "description": "Server type, tenant binding or required scopes missing." }, - "403": { + "404": { "content": { "application/json": { "schema": { @@ -64715,7 +65447,7 @@ } } }, - "description": "API client not authorized." + "description": "Tenant/event/module/feature unavailable." }, "429": { "content": { @@ -64725,57 +65457,83 @@ } } }, - "description": "Rate limit exceeded." + "description": "Read rate exceeded." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source tenant is frozen; audited reads cannot append history." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source gate could not be proven available." } } } }, - "/api/v1/public/{makerspace_slug}/machines/{id}": { + "/api/v1/events/{id}/": { "get": { - "operationId": "api_v1_public_machines_retrieve", + "operationId": "api_v1_events_retrieve_2", + "summary": "Read one tenant event (server HMAC; events:read)", "parameters": [ { "in": "header", - "name": "X-Nonce", + "name": "X-Client-Id", "schema": { "type": "string" }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "required": true }, { "in": "header", - "name": "X-Publishable-Key", + "name": "X-Nonce", "schema": { "type": "string" }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "description": "Required when nonce enforcement is enabled." }, { - "in": "path", - "name": "id", + "in": "header", + "name": "X-Timestamp", "schema": { - "type": "integer" + "type": "string" }, "required": true }, { "in": "path", - "name": "makerspace_slug", + "name": "id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "api" + "Server events" + ], + "security": [ + { + "ServerHMAC": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicMachineDetail" + "$ref": "#/components/schemas/IntegrationEvent" } } }, @@ -64789,7 +65547,7 @@ } } }, - "description": "" + "description": "Invalid cursor or page size." }, "401": { "content": { @@ -64799,7 +65557,7 @@ } } }, - "description": "" + "description": "Missing or invalid HMAC signature." }, "403": { "content": { @@ -64809,7 +65567,7 @@ } } }, - "description": "" + "description": "Server type, tenant binding or required scopes missing." }, "404": { "content": { @@ -64819,7 +65577,7 @@ } } }, - "description": "" + "description": "Tenant/event/module/feature unavailable." }, "429": { "content": { @@ -64829,58 +65587,100 @@ } } }, - "description": "" + "description": "Read rate exceeded." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source tenant is frozen; audited reads cannot append history." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source gate could not be proven available." } } } }, - "/api/v1/public/{makerspace_slug}/machines/details": { + "/api/v1/events/{id}/participants/": { "get": { - "operationId": "api_v1_public_machines_details_list", + "operationId": "api_v1_events_participants_retrieve", + "description": "Attendance dates are included only with participants:attendance:read. No contacts, answers, ticket/check-in tokens or payment fields.", + "summary": "Read minimal participants; events:read AND participants:read required", "parameters": [ + { + "in": "header", + "name": "X-Client-Id", + "schema": { + "type": "string" + }, + "required": true + }, { "in": "header", "name": "X-Nonce", "schema": { "type": "string" }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "description": "Required when nonce enforcement is enabled." }, { "in": "header", - "name": "X-Publishable-Key", + "name": "X-Timestamp", "schema": { "type": "string" }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "required": true }, { - "in": "path", - "name": "makerspace_slug", + "in": "query", + "name": "cursor", "schema": { "type": "string" }, + "description": "Opaque, signed continuation cursor." + }, + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, "required": true }, { - "name": "page", - "required": false, "in": "query", - "description": "A page number within the paginated result set.", + "name": "page_size", "schema": { "type": "integer" - } + }, + "description": "1–200; default 100." } ], "tags": [ - "api" + "Server events" + ], + "security": [ + { + "ServerHMAC": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PaginatedPublicMachineDetailList" + "$ref": "#/components/schemas/IntegrationParticipantPage" } } }, @@ -64894,7 +65694,7 @@ } } }, - "description": "" + "description": "Invalid cursor or page size." }, "401": { "content": { @@ -64904,7 +65704,7 @@ } } }, - "description": "" + "description": "Missing or invalid HMAC signature." }, "403": { "content": { @@ -64914,7 +65714,7 @@ } } }, - "description": "" + "description": "Server type, tenant binding or required scopes missing." }, "404": { "content": { @@ -64924,7 +65724,7 @@ } } }, - "description": "" + "description": "Tenant/event/module/feature unavailable." }, "429": { "content": { @@ -64934,33 +65734,97 @@ } } }, - "description": "" + "description": "Read rate exceeded." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source tenant is frozen; audited reads cannot append history." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Source gate could not be proven available." } } } }, - "/api/v1/public/{makerspace_slug}/membership-admission": { + "/api/v1/guest-admin/makerspace/{makerspace_id}/active-loans": { "get": { - "operationId": "api_v1_public_membership_admission_retrieve", + "operationId": "api_v1_guest_admin_makerspace_active_loans_list", + "summary": "List active loans awaiting return", "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "description": "Select one result within the authorized list.", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 9223372036854775807 + } + }, { "in": "path", - "name": "makerspace_slug", + "name": "makerspace_id", "schema": { - "type": "string" + "type": "integer" }, "required": true + }, + { + "name": "ordering", + "required": false, + "in": "query", + "description": "Which field to use when ordering the results.", + "schema": { + "type": "string" + } + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + }, + { + "name": "search", + "required": false, + "in": "query", + "description": "A search term (requested-for, requester name/email).", + "schema": { + "type": "string" + } } ], "tags": [ - "Memberships" + "Admin requests" + ], + "security": [ + { + "jwtAuth": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicAdmission" + "$ref": "#/components/schemas/PaginatedAdminRequestList" } } }, @@ -64974,7 +65838,7 @@ } } }, - "description": "" + "description": "Invalid request." }, "401": { "content": { @@ -64984,7 +65848,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -64994,7 +65858,7 @@ } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { @@ -65004,75 +65868,47 @@ } } }, - "description": "" - }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Not found." } } } }, - "/api/v1/public/{makerspace_slug}/membership-requests": { + "/api/v1/guest-admin/requests/{id}/return": { "post": { - "operationId": "api_v1_public_membership_requests_create", + "operationId": "api_v1_guest_admin_requests_return_create", + "summary": "Return issued request items", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "Memberships" + "Admin requests" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipRequestCreate" + "$ref": "#/components/schemas/ReturnRequest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MembershipRequestCreate" + "$ref": "#/components/schemas/ReturnRequest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MembershipRequestCreate" + "$ref": "#/components/schemas/ReturnRequest" } } - } + }, + "required": true }, "security": [ { @@ -65080,11 +65916,11 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MembershipOutcome" + "$ref": "#/components/schemas/AdminRequest" } } }, @@ -65098,7 +65934,7 @@ } } }, - "description": "" + "description": "Invalid request." }, "401": { "content": { @@ -65108,7 +65944,7 @@ } } }, - "description": "" + "description": "Authentication required." }, "403": { "content": { @@ -65118,7 +65954,7 @@ } } }, - "description": "" + "description": "Permission denied." }, "404": { "content": { @@ -65128,7 +65964,7 @@ } } }, - "description": "" + "description": "Not found." }, "409": { "content": { @@ -65138,9 +65974,9 @@ } } }, - "description": "" + "description": "Workflow conflict." }, - "423": { + "503": { "content": { "application/json": { "schema": { @@ -65148,40 +65984,202 @@ } } }, + "description": "Service unavailable." + } + } + } + }, + "/api/v1/health/": { + "get": { + "operationId": "api_v1_health_retrieve", + "summary": "Health check", + "tags": [ + "Health" + ], + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Health" + } + } + }, "description": "" }, - "429": { + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "503": { + "description": "The recovery gate is closed: the database is unreachable or a restore is running." + } + } + } + }, + "/api/v1/health/readiness/": { + "get": { + "operationId": "api_v1_health_readiness_retrieve", + "summary": "Readiness check", + "tags": [ + "Health" + ], + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/Readiness" } } }, "description": "" }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, "503": { + "description": "Not ready: host marker, database identity, migrations, fences or PII keys failed." + } + } + } + }, + "/api/v1/integrations/makerspace/{makerspace_id}/bounce-webhook": { + "post": { + "operationId": "api_v1_integrations_makerspace_bounce_webhook_create", + "description": "HMAC-SHA256 hex of ASCII timestamp + dot + raw JSON body. Five-minute replay window. Hard bounces/complaints only.", + "parameters": [ + { + "in": "header", + "name": "X-SpaceWorks-Signature", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "header", + "name": "X-SpaceWorks-Timestamp", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Notifications" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Bounce" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Bounce" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Bounce" + } + } + }, + "required": true + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/BounceResult" } } }, "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/public/{makerspace_slug}/my-bookings/": { + "/api/v1/integrations/makerspace/{makerspace_id}/delivery-logs": { "get": { - "operationId": "api_v1_public_my_bookings_retrieve", + "operationId": "api_v1_integrations_makerspace_delivery_logs_retrieve", "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "query", + "name": "channel", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "destination", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "event", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "feature", "schema": { "type": "string" + } + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, "required": true }, @@ -65194,21 +66192,14 @@ }, { "in": "query", - "name": "period", + "name": "status", "schema": { - "enum": [ - "upcoming", - "past" - ], - "type": "string", - "default": "upcoming", - "minLength": 1 - }, - "description": "* `upcoming` - upcoming\n* `past` - past" + "type": "string" + } } ], "tags": [ - "Member bookings" + "Notification delivery" ], "security": [ { @@ -65220,73 +66211,42 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberBookingList" + "$ref": "#/components/schemas/DeliveryPage" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Invalid request or bookings disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Sign-in required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Active account required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Makerspace or own booking not found." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } } }, - "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/cancel/": { + "/api/v1/integrations/makerspace/{makerspace_id}/delivery-logs/{kind}/{id}/retry": { "post": { - "operationId": "api_v1_public_my_bookings_cancel_create", + "operationId": "api_v1_integrations_makerspace_delivery_logs_retry_create", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "kind", "schema": { "type": "string" }, @@ -65294,37 +66254,35 @@ }, { "in": "path", - "name": "public_token", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Member bookings" + "Notification delivery" ], "requestBody": { "content": { - "type": { + "application/json": { "schema": { - "type": "object", - "additionalProperties": {}, - "description": "Unspecified request body" + "$ref": "#/components/schemas/Retry" } }, - "properties": { - "schema": {} + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Retry" + } }, - "additionalProperties": { + "multipart/form-data": { "schema": { - "type": "object", - "additionalProperties": {}, - "description": "Unspecified request body" + "$ref": "#/components/schemas/Retry" } } - } + }, + "required": true }, "security": [ { @@ -65336,140 +66294,67 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberBooking" + "$ref": "#/components/schemas/Delivery" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Invalid request or bookings disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Sign-in required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Active account required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Makerspace or own booking not found." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Tenant frozen for migration." + "description": "Makerspace writes are frozen for tenant migration." }, "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Tenant write gate unavailable." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Cancellation cutoff or state conflict." + "description": "A required dependency is unavailable." } } } }, - "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/reschedule/": { + "/api/v1/integrations/makerspace/{makerspace_id}/delivery-test": { "post": { - "operationId": "api_v1_public_my_bookings_reschedule_create", + "operationId": "api_v1_integrations_makerspace_delivery_test_create", "parameters": [ { "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" - }, - "required": true - }, - { - "in": "path", - "name": "public_token", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Member bookings" + "Notification delivery" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BookingReschedule" + "$ref": "#/components/schemas/DeliveryTest" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/BookingReschedule" + "$ref": "#/components/schemas/DeliveryTest" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/BookingReschedule" + "$ref": "#/components/schemas/DeliveryTest" } } - }, - "required": true + } }, "security": [ { @@ -65481,111 +66366,48 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberBooking" + "$ref": "#/components/schemas/DeliveryTestResult" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Invalid request or bookings disabled." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Sign-in required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Active account required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Makerspace or own booking not found." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Rate limit exceeded." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Cutoff, state or slot conflict." + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Tenant frozen." + "description": "Makerspace writes are frozen for tenant migration." }, "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Tenant gate unavailable." + "description": "A required dependency is unavailable." } } } }, - "/api/v1/public/{makerspace_slug}/my-bookings/calendar/": { + "/api/v1/integrations/makerspace/{makerspace_id}/email-suppressions": { "get": { - "operationId": "api_v1_public_my_bookings_calendar_retrieve", + "operationId": "api_v1_integrations_makerspace_email_suppressions_retrieve", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "makerspace_id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "Member bookings" + "Notification delivery" ], "security": [ { @@ -65597,15 +66419,12 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedState" + "$ref": "#/components/schemas/SuppressionPage" } } }, "description": "" }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." - }, "401": { "description": "Not signed in." }, @@ -65618,31 +66437,51 @@ } }, "post": { - "operationId": "api_v1_public_my_bookings_calendar_create", + "operationId": "api_v1_integrations_makerspace_email_suppressions_create", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "makerspace_id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "Member bookings" + "Notification delivery" ], - "security": [ - { - "jwtAuth": [] - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuppressionWrite" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/SuppressionWrite" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/SuppressionWrite" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/FeedIssued" + "$ref": "#/components/schemas/Suppression" } } }, @@ -65667,21 +66506,31 @@ "description": "A required dependency is unavailable." } } - }, + } + }, + "/api/v1/integrations/makerspace/{makerspace_id}/email-suppressions/{id}": { "delete": { - "operationId": "api_v1_public_my_bookings_calendar_destroy", + "operationId": "api_v1_integrations_makerspace_email_suppressions_destroy", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "id", "schema": { - "type": "string" + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" }, "required": true } ], "tags": [ - "Member bookings" + "Notification delivery" ], "security": [ { @@ -65690,10 +66539,7 @@ ], "responses": { "204": { - "description": "No response body" - }, - "400": { - "description": "Validation failed, or the makerspace module this route belongs to is off." + "description": "Suppression removed after operator review." }, "401": { "description": "Not signed in." @@ -65713,106 +66559,86 @@ } } }, - "/api/v1/public/{makerspace_slug}/presence-sessions": { - "post": { - "operationId": "api_v1_public_presence_sessions_create", + "/api/v1/integrations/makerspace/{makerspace_id}/timing-policy": { + "get": { + "operationId": "api_v1_integrations_makerspace_timing_policy_retrieve", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "makerspace_id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "Presence" + "Notifications" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PresenceStart" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/PresenceStart" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/PresenceStart" - } - } - }, - "required": true - }, "security": [ { "jwtAuth": [] } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PresenceSession" + "$ref": "#/components/schemas/TimingPolicy" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid input." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Membership permission required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "description": "Makerspace not found." - }, - "429": { - "description": "Rate limit exceeded." + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - } - }, - "/api/v1/public/{makerspace_slug}/presence-sessions/current": { - "get": { - "operationId": "api_v1_public_presence_sessions_current_retrieve", + }, + "patch": { + "operationId": "api_v1_integrations_makerspace_timing_policy_partial_update", "parameters": [ { "in": "path", - "name": "makerspace_slug", + "name": "makerspace_id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "Presence" + "Notifications" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedTimingPolicy" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedTimingPolicy" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedTimingPolicy" + } + } + } + }, "security": [ { "jwtAuth": [] @@ -65823,299 +66649,234 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PresenceCurrent" + "$ref": "#/components/schemas/TimingPolicy" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid input." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Membership permission required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "description": "Makerspace not found." + "description": "Not found, not visible to this actor, or the surface is not enabled here." }, - "429": { - "description": "Rate limit exceeded." + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." } } } }, - "/api/v1/public/{makerspace_slug}/presence-sessions/current/end": { + "/api/v1/integrations/messaging/twilio/m/{makerspace_id}/inbound": { "post": { - "operationId": "api_v1_public_presence_sessions_current_end_create", + "operationId": "api_v1_integrations_messaging_twilio_m_inbound_create", + "description": "Twilio form callback. Requires X-Twilio-Signature, AccountSid and bound sender number.", "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "header", + "name": "X-Twilio-Signature", "schema": { "type": "string" }, "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true } ], "tags": [ - "Presence" + "Messaging webhooks" ], - "security": [ - { - "jwtAuth": [] + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } } - ], + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PresenceCurrent" + "type": "object", + "additionalProperties": {} } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { + }, + "text/xml": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "string" } } }, - "description": "Invalid input." + "description": "" }, - "401": { - "description": "Authentication required." + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Membership permission required." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "description": "Makerspace not found." + "415": { + "description": "Unsupported media type for the request body." }, - "429": { - "description": "Rate limit exceeded." + "500": { + "description": "An internal or persistence failure prevented completing the request." } } } }, - "/api/v1/public/{makerspace_slug}/requests": { + "/api/v1/integrations/messaging/twilio/m/{makerspace_id}/status": { "post": { - "operationId": "api_v1_public_requests_create", - "summary": "Submit public borrow request", + "operationId": "api_v1_integrations_messaging_twilio_m_status_create", + "description": "Twilio form callback. Requires X-Twilio-Signature, AccountSid and bound sender number.", "parameters": [ { "in": "header", - "name": "Idempotency-Key", - "schema": { - "type": "string" - }, - "description": "Required for account-less submissions. Reusing a key with the same payload returns the original request; a different payload is rejected." - }, - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." - }, - { - "in": "header", - "name": "X-Publishable-Key", + "name": "X-Twilio-Signature", "schema": { "type": "string" }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "required": true }, { "in": "path", - "name": "makerspace_slug", + "name": "makerspace_id", "schema": { - "type": "string" + "type": "integer" }, "required": true } ], "tags": [ - "Public requests" + "Messaging webhooks" ], "requestBody": { "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RequestSubmit" - }, - "examples": { - "SubmitPublicEquipmentRequest": { - "value": { - "contact_name": "Shaan Shoukath", - "contact_email": "shaans@example.com", - "contact_phone": "+919876543210", - "requested_for": "Electronics workshop diagnostics", - "items": [ - { - "product_id": 42, - "quantity": 2 - } - ] - }, - "summary": "Submit public equipment request" - } - } - }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/RequestSubmit" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RequestSubmit" + "type": "object", + "additionalProperties": {} } } - }, - "required": true + } }, - "security": [ - { - "jwtAuth": [] - }, - {} - ], "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RequestSubmitResponse" - } - } - }, - "description": "" - }, - "400": { + "200": { "content": { "application/json": { "schema": { "type": "object", - "additionalProperties": {}, - "properties": { - "feature": { - "type": "string" - } - } + "additionalProperties": {} } - } - }, - "description": "Invalid submission or loans feature disabled." - }, - "401": { - "content": { - "application/json": { + }, + "text/xml": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "string" } } }, - "description": "Authentication required." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." + "415": { + "description": "Unsupported media type for the request body." }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + "500": { + "description": "An internal or persistence failure prevented completing the request." + } + } + } + }, + "/api/v1/integrations/messaging/twilio/platform/inbound": { + "post": { + "operationId": "api_v1_integrations_messaging_twilio_platform_inbound_create", + "description": "Twilio form callback. Requires X-Twilio-Signature, AccountSid and bound sender number.", + "parameters": [ + { + "in": "header", + "name": "X-Twilio-Signature", + "schema": { + "type": "string" }, - "description": "Workflow conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "required": true + } + ], + "tags": [ + "Messaging webhooks" + ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "additionalProperties": {} } - }, - "description": "Too many requests." - }, - "503": { + } + } + }, + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "object", + "additionalProperties": {} } - } - }, - "description": "Service unavailable." - }, - "423": { - "content": { - "application/json": { + }, + "text/xml": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "type": "string" } } }, - "description": "Tenant frozen for migration." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "415": { + "description": "Unsupported media type for the request body." + }, + "500": { + "description": "An internal or persistence failure prevented completing the request." } } } }, - "/api/v1/public/{makerspace_slug}/spaces/": { - "get": { - "operationId": "api_v1_public_spaces_list", + "/api/v1/integrations/messaging/twilio/platform/status": { + "post": { + "operationId": "api_v1_integrations_messaging_twilio_platform_status_create", + "description": "Twilio form callback. Requires X-Twilio-Signature, AccountSid and bound sender number.", "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "header", + "name": "X-Twilio-Signature", "schema": { "type": "string" }, @@ -66123,155 +66884,119 @@ } ], "tags": [ - "Public bookings" + "Messaging webhooks" ], + "requestBody": { + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + } + }, "responses": { "200": { - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicBookableSpace" - } - } - } - }, - "description": "" - }, - "400": { "content": { "application/json": { "schema": { "type": "object", "additionalProperties": {} } - } - }, - "description": "Invalid request." - }, - "404": { - "content": { - "application/json": { + }, + "text/xml": { "schema": { - "type": "object", - "additionalProperties": {} + "type": "string" } } }, - "description": "Space not found." + "description": "" }, - "429": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Rate limit exceeded." + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "415": { + "description": "Unsupported media type for the request body." + }, + "500": { + "description": "An internal or persistence failure prevented completing the request." } } } }, - "/api/v1/public/{makerspace_slug}/spaces/{public_token}/availability/": { + "/api/v1/integrations/messaging/whatsapp/m/{makerspace_id}": { "get": { - "operationId": "api_v1_public_spaces_availability_retrieve", + "operationId": "api_v1_integrations_messaging_whatsapp_m_retrieve", "parameters": [ { "in": "query", - "name": "ends_at", + "name": "hub.challenge", "schema": { - "type": "string", - "format": "date-time" + "type": "string" }, "required": true }, { - "in": "path", - "name": "makerspace_slug", + "in": "query", + "name": "hub.mode", "schema": { "type": "string" }, "required": true }, { - "in": "path", - "name": "public_token", + "in": "query", + "name": "hub.verify_token", "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, "required": true }, { - "in": "query", - "name": "starts_at", + "in": "path", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "date-time" + "type": "integer" }, "required": true } ], "tags": [ - "Public bookings" + "Messaging webhooks" ], "responses": { "200": { "content": { - "application/json": { + "text/plain": { "schema": { - "$ref": "#/components/schemas/PublicSpaceAvailability" + "type": "string" } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Invalid request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, - "404": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Space not found." + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "429": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Rate limit exceeded." + "500": { + "description": "An internal or persistence failure prevented completing the request." } } - } - }, - "/api/v1/public/{makerspace_slug}/spaces/{public_token}/book/": { + }, "post": { - "operationId": "api_v1_public_spaces_book_create", + "operationId": "api_v1_integrations_messaging_whatsapp_m_create", + "description": "Meta messages webhook; X-Hub-Signature-256 covers the exact raw JSON body.", "parameters": [ { - "in": "path", - "name": "makerspace_slug", + "in": "header", + "name": "X-Hub-Signature-256", "schema": { "type": "string" }, @@ -66279,76 +67004,40 @@ }, { "in": "path", - "name": "public_token", + "name": "makerspace_id", "schema": { - "type": "string", - "format": "uuid" + "type": "integer" }, "required": true } ], "tags": [ - "Public bookings" + "Messaging webhooks" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicBookingInput" + "type": "object", + "additionalProperties": {} } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicBookingInput" + "type": "object", + "additionalProperties": {} } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicBookingInput" + "type": "object", + "additionalProperties": {} } } - }, - "required": true - }, - "security": [ - { - "jwtAuth": [] } - ], + }, "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicBookingResponse" - } - } - }, - "description": "" - }, - "400": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Invalid request." - }, - "404": { - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": {} - } - } - }, - "description": "Space not found." - }, - "429": { + "200": { "content": { "application/json": { "schema": { @@ -66357,140 +67046,84 @@ } } }, - "description": "Rate limit exceeded." + "description": "" }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication is required." + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Active account and any existing membership waiver acceptance are required." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Booking conflict." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "423": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Tenant frozen for migration." + "415": { + "description": "Unsupported media type for the request body." }, - "503": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Tenant write gate unavailable." + "500": { + "description": "An internal or persistence failure prevented completing the request." } } } }, - "/api/v1/public/{makerspace_slug}/stats/": { + "/api/v1/integrations/messaging/whatsapp/platform": { "get": { - "operationId": "api_v1_public_stats_retrieve", - "description": "Get public activity stats for a public makerspace.", - "summary": "Get public makerspace stats", + "operationId": "api_v1_integrations_messaging_whatsapp_platform_retrieve", "parameters": [ { - "in": "header", - "name": "X-Nonce", + "in": "query", + "name": "hub.challenge", "schema": { "type": "string" }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "required": true }, { - "in": "header", - "name": "X-Publishable-Key", + "in": "query", + "name": "hub.mode", "schema": { "type": "string" }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "required": true }, { - "in": "path", - "name": "makerspace_slug", + "in": "query", + "name": "hub.verify_token", "schema": { "type": "string" }, - "description": "Public makerspace code (for example TSEL) or slug.", "required": true } ], "tags": [ - "Public inventory" - ], - "security": [ - { - "jwtAuth": [] - }, - {} + "Messaging webhooks" ], "responses": { "200": { "content": { - "application/json": { + "text/plain": { "schema": { - "$ref": "#/components/schemas/PublicStats" + "type": "string" } } }, "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "500": { + "description": "An internal or persistence failure prevented completing the request." } } - } - }, - "/api/v1/public/{makerspace_slug}/tools/checklist": { + }, "post": { - "operationId": "api_v1_public_tools_checklist_create", - "summary": "Preview kit components for a scanned self-checkout tool", + "operationId": "api_v1_integrations_messaging_whatsapp_platform_create", + "description": "Meta messages webhook; X-Hub-Signature-256 covers the exact raw JSON body.", "parameters": [ { "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." - }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" - }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." - }, - { - "in": "path", - "name": "makerspace_slug", + "name": "X-Hub-Signature-256", "schema": { "type": "string" }, @@ -66498,158 +67131,78 @@ } ], "tags": [ - "Public requests" + "Messaging webhooks" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MemberKitPreviewInput" + "type": "object", + "additionalProperties": {} } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/MemberKitPreviewInput" + "type": "object", + "additionalProperties": {} } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/MemberKitPreviewInput" + "type": "object", + "additionalProperties": {} } } - }, - "required": true - }, - "security": [ - { - "jwtAuth": [] } - ], + }, "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/KitPreview" + "type": "object", + "additionalProperties": {} } } }, "description": "" }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" - }, - "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "415": { + "description": "Unsupported media type for the request body." }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "" + "500": { + "description": "An internal or persistence failure prevented completing the request." } } } }, - "/api/v1/public/{makerspace_slug}/tools/checkout": { + "/api/v1/integrations/push/devices": { "post": { - "operationId": "api_v1_public_tools_checkout_create", - "summary": "Check out a public tool by QR", - "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." - }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" - }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." - }, - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" - }, - "required": true - } - ], + "operationId": "api_v1_integrations_push_devices_create", "tags": [ - "Public requests" + "Native push" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicToolCheckout" - }, - "examples": { - "PublicQRToolCheckout": { - "value": { - "payload": "BOX-ABC123", - "requester_name": "Shaan Shoukath", - "contact_email": "shaans@example.com", - "contact_phone": "+919876543210", - "evidence_id": 122, - "remark": "Borrowing for electronics workshop diagnostics." - }, - "summary": "Public QR tool checkout" - } + "$ref": "#/components/schemas/PushDeviceRegistration" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicToolCheckout" + "$ref": "#/components/schemas/PushDeviceRegistration" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicToolCheckout" + "$ref": "#/components/schemas/PushDeviceRegistration" } } }, @@ -66661,57 +67214,73 @@ } ], "responses": { - "201": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicToolLoan" + "$ref": "#/components/schemas/PushDevice" } } }, "description": "" }, - "400": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/PushDevice" } } }, - "description": "Invalid request." + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, - "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } + "409": { + "description": "Conflicts with the current state." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/integrations/push/devices/{device_id}": { + "delete": { + "operationId": "api_v1_integrations_push_devices_destroy", + "parameters": [ + { + "in": "path", + "name": "device_id", + "schema": { + "type": "integer" }, - "description": "Not found." + "required": true + } + ], + "tags": [ + "Native push" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" }, - "409": { + "401": { "content": { "application/json": { "schema": { @@ -66719,9 +67288,9 @@ } } }, - "description": "Workflow conflict." + "description": "" }, - "429": { + "403": { "content": { "application/json": { "schema": { @@ -66729,9 +67298,9 @@ } } }, - "description": "Too many requests." + "description": "" }, - "503": { + "404": { "content": { "application/json": { "schema": { @@ -66739,59 +67308,33 @@ } } }, - "description": "Service unavailable." + "description": "" } } } }, - "/api/v1/public/{makerspace_slug}/tools/evidence-url": { + "/api/v1/integrations/telegram/test-alert": { "post": { - "operationId": "api_v1_public_tools_evidence_url_create", - "summary": "Create a public self-checkout evidence upload URL", - "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" - }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." - }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" - }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." - }, - { - "in": "path", - "name": "makerspace_slug", - "schema": { - "type": "string" - }, - "required": true - } - ], + "operationId": "api_v1_integrations_telegram_test_alert_create", + "summary": "Send Telegram test alert", "tags": [ - "Public requests" + "Telegram" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicToolEvidenceUrlRequest" + "$ref": "#/components/schemas/TelegramTestAlert" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicToolEvidenceUrlRequest" + "$ref": "#/components/schemas/TelegramTestAlert" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicToolEvidenceUrlRequest" + "$ref": "#/components/schemas/TelegramTestAlert" } } }, @@ -66803,148 +67346,169 @@ } ], "responses": { - "201": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EvidenceUrlResponse" - } - } - }, - "description": "" + "200": { + "description": "Delivery attempt result; `delivered: false` with a reason when Telegram is unconfigured or rejects the message." }, "400": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Invalid request." + "description": "Validation failed, or the makerspace module this route belongs to is off." }, "401": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Authentication required." + "description": "Not signed in." }, "403": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Permission denied." + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." }, "404": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/integrations/telegram/webhook": { + "post": { + "operationId": "api_v1_integrations_telegram_webhook_create", + "description": "Retained so an already-registered webhook does not retry forever. Callback queries are acknowledged and discarded: accepting and rejecting borrow requests happens in the staff console, never from chat.", + "summary": "Acknowledge a Telegram webhook (no action is taken)", + "tags": [ + "Telegram" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TelegramWebhook" } }, - "description": "Workflow conflict." - }, - "429": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/TelegramWebhook" } }, - "description": "Too many requests." + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/TelegramWebhook" + } + } + } + }, + "responses": { + "200": { + "description": "Acknowledged; no action taken." }, - "503": { + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "403": { + "description": "TELEGRAM_WEBHOOK_SECRET is unset, or the secret-token header is wrong." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + } + } + } + }, + "/api/v1/internal/cron/return-reminders": { + "post": { + "operationId": "api_v1_internal_cron_return_reminders_create", + "tags": [ + "Health" + ], + "responses": { + "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HardwareRequestError" + "$ref": "#/components/schemas/ReturnReminderCronResponse" } } }, - "description": "Service unavailable." + "description": "Return reminder run result." + }, + "403": { + "description": "Invalid cron secret." + }, + "404": { + "description": "Cron endpoint is not configured." } } } }, - "/api/v1/public/{makerspace_slug}/tools/return": { - "post": { - "operationId": "api_v1_public_tools_return_create", - "summary": "Return a public tool by QR", + "/api/v1/internal/tls-check": { + "get": { + "operationId": "api_v1_internal_tls_check_retrieve", + "summary": "Check whether on-demand TLS may be issued for a domain", "parameters": [ { - "in": "header", - "name": "X-Nonce", + "in": "query", + "name": "domain", "schema": { "type": "string" }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "description": "Canonical hostname requested for on-demand TLS issuance.", + "required": true + } + ], + "tags": [ + "Internal" + ], + "responses": { + "200": { + "description": "TLS issuance is allowed." + }, + "403": { + "description": "TLS issuance is denied." + }, + "400": { + "description": "Managed mode: the Host header is not an infra host." }, + "503": { + "description": "The recovery gate is closed; deny issuance." + } + } + } + }, + "/api/v1/kiosk-stations/{public_token}/commands/online/": { + "post": { + "operationId": "api_v1_kiosk_stations_commands_online_create", + "summary": "Run one online kiosk command through the workflow", + "parameters": [ { "in": "header", - "name": "X-Publishable-Key", + "name": "X-Kiosk-Operator-Lease", "schema": { "type": "string" }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "description": "Signed operator lease returned by the operator-lease route.", + "required": true }, { "in": "path", - "name": "makerspace_slug", + "name": "public_token", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Public requests" + "Kiosk stations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicToolScan" - }, - "examples": { - "PublicQRToolReturnScan": { - "value": { - "identifier": "shaans@example.com", - "payload": "BOX-ABC123", - "evidence_id": 123, - "remark": "Returned to the electronics shelf in good condition." - }, - "summary": "Public QR tool return scan" - } + "$ref": "#/components/schemas/KioskOnlineCommand" } }, "application/x-www-form-urlencoded": { "schema": { - "$ref": "#/components/schemas/PublicToolScan" + "$ref": "#/components/schemas/KioskOnlineCommand" } }, "multipart/form-data": { "schema": { - "$ref": "#/components/schemas/PublicToolScan" + "$ref": "#/components/schemas/KioskOnlineCommand" } } }, @@ -66960,7 +67524,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicToolLoan" + "$ref": "#/components/schemas/KioskOnlineCommandResult" } } }, @@ -66974,7 +67538,7 @@ } } }, - "description": "Invalid request." + "description": "Invalid payload or capability disabled." }, "401": { "content": { @@ -66984,7 +67548,7 @@ } } }, - "description": "Authentication required." + "description": "Operator is not signed in." }, "403": { "content": { @@ -66994,7 +67558,7 @@ } } }, - "description": "Permission denied." + "description": "Invalid Station session, lease or authority." }, "404": { "content": { @@ -67004,17 +67568,7 @@ } } }, - "description": "Not found." - }, - "409": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HardwareRequestError" - } - } - }, - "description": "Workflow conflict." + "description": "Target not found in this makerspace." }, "429": { "content": { @@ -67024,7 +67578,7 @@ } } }, - "description": "Too many requests." + "description": "Request rate limit exceeded." }, "503": { "content": { @@ -67034,15 +67588,25 @@ } } }, - "description": "Service unavailable." + "description": "Storage unavailable." } } } }, - "/api/v1/public/machine-service/3d-printer/requests/{public_token}/status": { - "get": { - "operationId": "api_v1_public_machine_service_3d_printer_requests_status_retrieve", + "/api/v1/kiosk-stations/{public_token}/evidence/prepare/": { + "post": { + "operationId": "api_v1_kiosk_stations_evidence_prepare_create", + "summary": "Presign a direct-to-staging evidence upload", "parameters": [ + { + "in": "header", + "name": "X-Kiosk-Operator-Lease", + "schema": { + "type": "string" + }, + "description": "Signed operator lease returned by the operator-lease route.", + "required": true + }, { "in": "path", "name": "public_token", @@ -67054,63 +67618,39 @@ } ], "tags": [ - "Public machine service" + "Kiosk stations" ], - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicPrinterStatus" - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KioskEvidencePrepare" } }, - "description": "" - } - } - } - }, - "/api/v1/public/makerspaces/": { - "get": { - "operationId": "api_v1_public_makerspaces_list", - "description": "List makerspaces that have a public site enabled.", - "summary": "List public makerspaces", - "parameters": [ - { - "in": "header", - "name": "X-Nonce", - "schema": { - "type": "string" + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/KioskEvidencePrepare" + } }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/KioskEvidencePrepare" + } + } }, - { - "in": "header", - "name": "X-Publishable-Key", - "schema": { - "type": "string" - }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." - } - ], - "tags": [ - "Public inventory" - ], + "required": true + }, "security": [ { "jwtAuth": [] - }, - {} + } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicMakerspace" - } + "$ref": "#/components/schemas/EvidenceUrlResponse" } } }, @@ -67120,149 +67660,230 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Public inventory module or loans feature is disabled." + "description": "Invalid payload or capability disabled." }, "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid authentication credentials." + "description": "Operator is not signed in." }, "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Public API client is not authorized." + "description": "Invalid Station session, lease or authority." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Item or public makerspace not found, or hidden by edition." + "description": "Target not found in this makerspace." }, "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicInventoryError" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Public read rate limit exceeded." + "description": "Request rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Storage unavailable." } } } }, - "/api/v1/public/organizations/{slug}/": { - "get": { - "operationId": "api_v1_public_organizations_retrieve", - "summary": "Retrieve a public organization profile", + "/api/v1/kiosk-stations/{public_token}/operator-lease/": { + "post": { + "operationId": "api_v1_kiosk_stations_operator_lease_create", + "summary": "Issue a human operator lease on this Station session", "parameters": [ { "in": "path", - "name": "slug", + "name": "public_token", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Public organizations" + "Kiosk stations" + ], + "security": [ + { + "jwtAuth": [] + } ], "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicOrganization" + "$ref": "#/components/schemas/KioskOperatorLease" } } }, "description": "" }, - "404": { - "description": "Organization not found." + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid Station credential, session, origin, feature or lease." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid Station credential, session, origin, feature or lease." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid Station credential, session, origin, feature or lease." }, "429": { - "description": "Rate limit exceeded." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid Station credential, session, origin, feature or lease." } } - } - }, - "/api/v1/public/organizations/{slug}/events/": { - "get": { - "operationId": "api_v1_public_organizations_events_retrieve", - "summary": "List public events organized across makerspaces", + }, + "delete": { + "operationId": "api_v1_kiosk_stations_operator_lease_destroy", + "summary": "Revoke the caller's operator lease (operator sign-out)", "parameters": [ { - "in": "path", - "name": "slug", + "in": "header", + "name": "X-Kiosk-Operator-Lease", "schema": { "type": "string" }, + "description": "Signed operator lease returned by the operator-lease route.", + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, "required": true } ], "tags": [ - "Public organizations" + "Kiosk stations" + ], + "security": [ + { + "jwtAuth": [] + } ], "responses": { - "200": { + "204": { + "description": "No response body" + }, + "401": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicOrganizationEventList" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - }, - "404": { - "description": "Organization not found." + "description": "Invalid Station credential, session, origin, feature or lease." }, - "429": { - "description": "Rate limit exceeded." + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid Station credential, session, origin, feature or lease." } } } }, - "/api/v1/public/requests/{public_token}/status": { - "get": { - "operationId": "api_v1_public_requests_status_retrieve", - "summary": "Get request status by public token", + "/api/v1/kiosk-stations/{public_token}/replay/{client_command_id}/execute/": { + "post": { + "operationId": "api_v1_kiosk_stations_replay_execute_create", + "description": "Same Station cookie, origin/CSRF and human lease as prepare. Current authority and domain rules are checked again; duplicate success returns stable references.", + "summary": "Execute or recover one prepared handover command", "parameters": [ { "in": "header", - "name": "X-Nonce", + "name": "X-Kiosk-Operator-Lease", "schema": { "type": "string" }, - "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + "description": "Signed operator lease returned by the operator-lease route.", + "required": true }, { "in": "header", - "name": "X-Publishable-Key", + "name": "X-Station-CSRF", "schema": { "type": "string" }, - "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + "required": true + }, + { + "in": "path", + "name": "client_command_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true }, { "in": "path", @@ -67275,38 +67896,36 @@ } ], "tags": [ - "Public requests" + "Kiosk stations" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReplayEnvelope" + } + } + }, + "required": true + }, + "security": [ + { + "KioskStationCookie": [] + }, + {} ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicRequestStatus" - }, - "examples": { - "PublicRequestStatus": { - "value": { - "public_token": "4f2b93e1-6ef4-41c2-8407-7f26bb3b2d8f", - "requested_for": "Electronics workshop diagnostics", - "status": "pending_approval", - "rejection_reason": "", - "created_at": "2026-06-11T10:30:00Z", - "items": [ - { - "product_name": "Soldering Iron", - "requested_quantity": 2 - } - ] - }, - "summary": "Public request status" - } + "$ref": "#/components/schemas/ReplayResult" } } }, "description": "" }, - "401": { + "400": { "content": { "application/json": { "schema": { @@ -67314,7 +67933,7 @@ } } }, - "description": "Authentication required." + "description": "Invalid command, target, module or feature disabled." }, "403": { "content": { @@ -67324,9 +67943,9 @@ } } }, - "description": "Permission denied." + "description": "Station, origin, session, operator lease or live permission refused." }, - "404": { + "409": { "content": { "application/json": { "schema": { @@ -67334,9 +67953,9 @@ } } }, - "description": "Not found." + "description": "Ownership conflict, expired command, predecessor refusal or concurrent claim." }, - "429": { + "423": { "content": { "application/json": { "schema": { @@ -67344,63 +67963,72 @@ } } }, - "description": "Too many requests." - } - } - } - }, - "/api/v1/recovery": { - "get": { - "operationId": "api_v1_recovery_retrieve", - "summary": "Get deployment quarantine and residual-risk state", - "tags": [ - "Backup recovery" - ], - "security": [ - { - "jwtAuth": [] - } - ], - "responses": { - "200": { + "description": "Tenant migration source gate is closed." + }, + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecoveryState" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "" - }, - "401": { - "description": "Authentication is required." + "description": "Replay rate limit exceeded." }, - "403": { - "description": "The authenticated actor is not authorized." + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Storage or replay temporarily unavailable." } } - }, - "post": { - "operationId": "api_v1_recovery_create", - "summary": "Acknowledge residual risk and lift quarantine", + } + }, + "/api/v1/kiosk-stations/{public_token}/replay/prepare/": { + "post": { + "operationId": "api_v1_kiosk_stations_replay_prepare_create", + "description": "Requires the path-bound Station cookie, exact Origin/Referer, X-Station-CSRF and signed human lease; no browser login required. Send the identical envelope to execute.", + "summary": "Prepare an ordered command and staging evidence upload", + "parameters": [ + { + "in": "header", + "name": "X-Kiosk-Operator-Lease", + "schema": { + "type": "string" + }, + "description": "Signed operator lease returned by the operator-lease route.", + "required": true + }, + { + "in": "header", + "name": "X-Station-CSRF", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "Backup recovery" + "Kiosk stations" ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecoveryAcknowledge" - } - }, - "application/x-www-form-urlencoded": { - "schema": { - "$ref": "#/components/schemas/RecoveryAcknowledge" - } - }, - "multipart/form-data": { - "schema": { - "$ref": "#/components/schemas/RecoveryAcknowledge" + "$ref": "#/components/schemas/ReplayEnvelope" } } }, @@ -67408,305 +68036,21286 @@ }, "security": [ { - "jwtAuth": [] - } + "KioskStationCookie": [] + }, + {} ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RecoveryState" + "$ref": "#/components/schemas/ReplayResult" } } }, "description": "" }, "400": { - "description": "The request is invalid for the current lifecycle state." - }, - "401": { - "description": "Authentication is required." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid command, target, module or feature disabled." }, "403": { - "description": "The authenticated actor is not authorized." + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Station, origin, session, operator lease or live permission refused." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Ownership conflict, expired command, predecessor refusal or concurrent claim." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant migration source gate is closed." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Replay rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Storage or replay temporarily unavailable." } } } }, - "/api/v1/webhooks/razorpay/{public_code}": { + "/api/v1/kiosk-stations/{public_token}/session/": { "post": { - "operationId": "api_v1_webhooks_razorpay_create", - "description": "Verifies the X-Razorpay-Signature HMAC over request.body using the addressed makerspace's webhook secret before applying an idempotent event.", - "summary": "Receive a makerspace Razorpay webhook", + "operationId": "api_v1_kiosk_stations_session_create", + "summary": "Exchange a kiosk Station PIN for a Station session", "parameters": [ { "in": "path", - "name": "public_code", + "name": "public_token", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Payments" + "Kiosk stations" ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KioskStationPin" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/KioskStationPin" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/KioskStationPin" + } + } + }, + "required": true + }, "responses": { - "200": { + "201": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookResponse" + "$ref": "#/components/schemas/KioskStationBootstrap" } } }, - "description": "Verified event acknowledged (acted on or deliberately ignored)." + "description": "" }, "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid signature, payload, or configuration." + "description": "Invalid Station credential, session, origin, feature or lease." }, - "404": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace was not found." + "description": "Invalid Station credential, session, origin, feature or lease." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid Station credential, session, origin, feature or lease." } } - } - }, - "/api/v1/webhooks/stripe/{public_code}": { - "post": { - "operationId": "api_v1_webhooks_stripe_create", - "description": "Verifies the Stripe signature using request.body and the addressed makerspace's webhook secret before applying an idempotent event.", - "summary": "Receive a makerspace Stripe webhook", + }, + "delete": { + "operationId": "api_v1_kiosk_stations_session_destroy", + "summary": "End the Station session and revoke its operator leases", "parameters": [ { "in": "path", - "name": "public_code", + "name": "public_token", "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true } ], "tags": [ - "Payments" + "Kiosk stations" + ], + "security": [ + { + "KioskStationCookie": [] + } ], "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WebhookResponse" - } - } - }, - "description": "Verified event acknowledged (acted on or deliberately ignored)." + "204": { + "description": "No response body" }, - "400": { + "403": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid signature, payload, or configuration." + "description": "Invalid Station credential, session, origin, feature or lease." }, - "404": { + "429": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace was not found." + "description": "Invalid Station credential, session, origin, feature or lease." } } } }, - "/api/v1/webhooks/stripe/connect": { + "/api/v1/kiosk-stations/{public_token}/work-package/": { "post": { - "operationId": "api_v1_webhooks_stripe_connect_create", - "summary": "Receive a platform Stripe Connect webhook", + "operationId": "api_v1_kiosk_stations_work_package_create", + "summary": "Load the minimal work package for one handover", + "parameters": [ + { + "in": "header", + "name": "X-Kiosk-Operator-Lease", + "schema": { + "type": "string" + }, + "description": "Signed operator lease returned by the operator-lease route.", + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], "tags": [ - "Payments" + "Kiosk stations" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KioskWorkPackageRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/KioskWorkPackageRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/KioskWorkPackageRequest" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } ], "responses": { "200": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookResponse" + "$ref": "#/components/schemas/KioskWorkPackage" } } }, - "description": "Verified event acknowledged (acted on or deliberately ignored)." + "description": "" }, "400": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Invalid signature, payload, or configuration." + "description": "Invalid payload or capability disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Operator is not signed in." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid Station session, lease or authority." }, "404": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookResponse" + "$ref": "#/components/schemas/HardwareRequestError" } } }, - "description": "Makerspace was not found." + "description": "Target not found in this makerspace." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." } } } - } - }, - "components": { - "schemas": { - "AcceptQuantity": { - "type": "object", - "properties": { - "item_id": { - "type": "integer" - }, - "quantity": { - "type": "integer", - "minimum": 0 + }, + "/api/v1/live/": { + "get": { + "operationId": "api_v1_live_retrieve", + "description": "text/event-stream of `change` events `{kind, makerspace_id, target_type, target_id, actor_id, ts}` for every makerspace the session may see. Clients invalidate cached queries on an event and refetch; no tenant content travels on the stream. 503 when the deployment has no Redis.", + "summary": "Live update stream (Server-Sent Events)", + "tags": [ + "Health" + ], + "security": [ + { + "jwtAuth": [] } - }, - "required": [ - "item_id", - "quantity" - ] - }, - "AcceptRequest": { - "type": "object", - "properties": { - "accepted_quantities": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AcceptQuantity" - } + ], + "responses": { + "200": { + "content": { + "text/event-stream": { + "schema": { + "type": "string" + } + } + }, + "description": "" + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Account access is restricted." + }, + "503": { + "description": "No usable Redis (unset, misconfigured or unreachable); poll instead." } } - }, - "AccessLevelEnum": { - "enum": [ - "operate", - "manage", - "full" + } + }, + "/api/v1/me/messaging": { + "get": { + "operationId": "api_v1_me_messaging_retrieve", + "parameters": [ + { + "in": "query", + "name": "makerspace", + "schema": { + "type": "integer" + }, + "required": true + } ], - "type": "string", - "description": "* `operate` - Operate\n* `manage` - Manage\n* `full` - Full" - }, - "AccessStatusEnum": { - "enum": [ - "active", - "restricted", - "suspended" + "tags": [ + "Messaging" ], - "type": "string", - "description": "* `active` - Active\n* `restricted` - Restricted\n* `suspended` - Suspended" - }, - "AccountFailure": { - "type": "object", - "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" + "security": [ + { + "jwtAuth": [] } - }, - "required": [ - "detail" - ] - }, - "AccountProfile": { - "type": "object", - "properties": { - "display_name": { - "type": "string", - "maxLength": 200 + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberMessaging" + } + } + }, + "description": "" }, - "first_name": { - "type": "string", - "maxLength": 150 + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, - "last_name": { - "type": "string", - "maxLength": 150 + "401": { + "description": "Not signed in." }, - "phone": { - "type": "string", - "maxLength": 32 + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." } } - }, - "AccountSession": { - "type": "object", - "properties": { - "id": { - "type": "string" + } + }, + "/api/v1/me/messaging/consent": { + "post": { + "operationId": "api_v1_me_messaging_consent_create", + "tags": [ + "Messaging" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConsentWrite" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ConsentWrite" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ConsentWrite" + } + } }, - "kind": { - "$ref": "#/components/schemas/AccountSessionKindEnum" + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberMessaging" + } + } + }, + "description": "" }, - "created_at": { - "type": "string", - "format": "date-time" + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." }, - "expires_at": { - "type": "string", - "format": "date-time", - "nullable": true + "401": { + "description": "Not signed in." }, - "last_used_at": { - "type": "string", - "format": "date-time", + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "409": { + "description": "Conflicts with the current state." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/member/archived-payments": { + "get": { + "operationId": "api_v1_member_archived_payments_list", + "description": "Lists archived makerspaces where the caller retains an active membership and has payment history available to read or settle.", + "summary": "Discover the caller's archived makerspace payments", + "tags": [ + "Payments" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ArchivedPaymentSummary" + } + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/cancel": { + "post": { + "operationId": "api_v1_member_makerspaces_account_cancel_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MembershipReason" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MembershipReason" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MembershipReason" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MembershipAccount" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/exports": { + "get": { + "operationId": "api_v1_member_makerspaces_account_exports_retrieve", + "summary": "List the caller's personal exports", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubjectExportList" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + }, + "post": { + "operationId": "api_v1_member_makerspaces_account_exports_create", + "summary": "Request a personal ZIP/JSON data export", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubjectExport" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/exports/{job_id}": { + "get": { + "operationId": "api_v1_member_makerspaces_account_exports_retrieve_2", + "summary": "Poll the caller's personal export", + "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubjectExport" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/exports/{job_id}/download": { + "post": { + "operationId": "api_v1_member_makerspaces_account_exports_download_create", + "summary": "Sign a short-lived personal export download URL", + "parameters": [ + { + "in": "path", + "name": "job_id", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DataExportDownloadUrl" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/leave": { + "post": { + "operationId": "api_v1_member_makerspaces_account_leave_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MembershipReason" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MembershipReason" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MembershipReason" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MembershipAccount" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/membership": { + "get": { + "operationId": "api_v1_member_makerspaces_account_membership_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MembershipAccount" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/profile": { + "get": { + "operationId": "api_v1_member_makerspaces_account_profile_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountProfile" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + }, + "patch": { + "operationId": "api_v1_member_makerspaces_account_profile_partial_update", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedAccountProfile" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedAccountProfile" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedAccountProfile" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountProfile" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/sessions": { + "get": { + "operationId": "api_v1_member_makerspaces_account_sessions_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountSessionPage" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/account/sessions/{session_id}": { + "delete": { + "operationId": "api_v1_member_makerspaces_account_sessions_destroy", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "session_id", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Member account" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + } + } + } + }, + "description": "Disabled module/feature, invalid input, or unavailable export." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AccountFailure" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/activity": { + "get": { + "operationId": "api_v1_member_makerspaces_activity_retrieve", + "summary": "Retrieve the caller's makerspace activity", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member activity" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberActivity" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Membership module disabled." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "An active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Makerspace not found." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/admission": { + "get": { + "operationId": "api_v1_member_makerspaces_admission_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdmissionRequest" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/admission/{request_id}/cancel": { + "post": { + "operationId": "api_v1_member_makerspaces_admission_cancel_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "request_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdmissionRequest" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/admission/{request_id}/vouch": { + "post": { + "operationId": "api_v1_member_makerspaces_admission_vouch_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "request_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdmissionVouchOutcome" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/admission/export": { + "get": { + "operationId": "api_v1_member_makerspaces_admission_export_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdmissionExport" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/certifications": { + "get": { + "operationId": "api_v1_member_makerspaces_certifications_retrieve", + "summary": "List your own certification history", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + }, + "description": "Page number (24 rows per page)." + } + ], + "tags": [ + "Member certifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCertificationPage" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/collaborative-events/": { + "get": { + "operationId": "api_v1_member_makerspaces_collaborative_events_list", + "summary": "List events hosted by accepted collaborators", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CollaborativeEvent" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid event request." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Collaborative event not found." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/collaborative-events/{id}/register/": { + "post": { + "operationId": "api_v1_member_makerspaces_collaborative_events_register_create", + "description": "Invite-only admission can resolve a matching verified account email when the code is omitted. Partner eligibility, host waiver and normal admission rules still apply.", + "summary": "Register for a collaborative event", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CollaborativeEventRegistrationInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/CollaborativeEventRegistrationInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/CollaborativeEventRegistrationInput" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicEventRegistrationResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid event request." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Collaborative event not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/directory": { + "get": { + "operationId": "api_v1_member_makerspaces_directory_retrieve", + "summary": "List members who published a profile", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Match on username, display name, headline or institution. Contact details are never searched." + } + ], + "tags": [ + "Member profile" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Directory" + } + } + }, + "description": "" + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "An active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/directory/{membership_id}": { + "get": { + "operationId": "api_v1_member_makerspaces_directory_retrieve_2", + "summary": "Retrieve another member's published profile", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "membership_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileRead" + } + } + }, + "description": "" + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "An active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-calendar-feed/": { + "get": { + "operationId": "api_v1_member_makerspaces_event_calendar_feed_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCalendarFeedState" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + }, + "post": { + "operationId": "api_v1_member_makerspaces_event_calendar_feed_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCalendarFeedIssue" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberCalendarFeedIssue" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberCalendarFeedIssue" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCalendarFeedIssued" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Concurrent rotation conflict." + } + } + }, + "delete": { + "operationId": "api_v1_member_makerspaces_event_calendar_feed_destroy", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-certificates/{id}/download/": { + "get": { + "operationId": "api_v1_member_makerspaces_event_certificates_download_retrieve", + "description": "Private audited certificate download; configurable renders are bounded to 30/hour per account.", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CertificateDownload" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "410": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Certificate revoked." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Certificate storage unavailable." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid feedback answers." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback resource not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback or certificate state conflict." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/calendar.ics": { + "get": { + "operationId": "api_v1_member_makerspaces_event_registrations_calendar.ics_retrieve_2", + "description": "Exact row ownership and historical admission audience authorize this read; current organization eligibility is not required. No access to other member history.", + "summary": "Download one owned registration calendar", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "text/calendar": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/cancel/": { + "post": { + "operationId": "api_v1_member_makerspaces_event_registrations_cancel_create", + "description": "Releases pending charges and uses the event's existing waitlist promotion policy. Settled charges are retained; this action does not issue a refund. Members may cancel only before the event's cancellation cutoff (null means start).", + "summary": "Cancel the caller's event registration", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberRegistrationCancelResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid body or events module disabled (field-keyed errors)." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "An active account owning an account/invite registration, or an active membership, is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Owned registration not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Registration cannot be cancelled." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant frozen for migration." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant write gate unavailable." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/feedback/": { + "get": { + "operationId": "api_v1_member_makerspaces_event_registrations_feedback_retrieve", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackForm" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid feedback answers." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback resource not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback or certificate state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + }, + "post": { + "operationId": "api_v1_member_makerspaces_event_registrations_feedback_create", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackSubmission" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/FeedbackSubmission" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/FeedbackSubmission" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackSubmissionResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid feedback answers." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback resource not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback or certificate state conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/qr": { + "get": { + "operationId": "api_v1_member_makerspaces_event_registrations_qr_retrieve", + "summary": "Render the caller's event check-in QR code", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member activity" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "image/svg+xml": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "Check-in QR code as SVG." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active membership or an owned account/invite registration is required." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Registration not found." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/ticket/": { + "get": { + "operationId": "api_v1_member_makerspaces_event_registrations_ticket_retrieve", + "summary": "Read the caller's event ticket", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member activity" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberEventTicket" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/{id}/ticket.png": { + "get": { + "operationId": "api_v1_member_makerspaces_event_registrations_ticket.png_retrieve", + "summary": "Download the caller's registered or attended ticket", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member activity" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/event-registrations/calendar.ics": { + "get": { + "operationId": "api_v1_member_makerspaces_event_registrations_calendar.ics_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "text/calendar": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "RFC 5545 calendar (`text/calendar; charset=utf-8`)." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active membership required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Makerspace not found." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Events module unavailable." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/loans/{id}/renew": { + "post": { + "operationId": "api_v1_member_makerspaces_loans_renew_create", + "summary": "Extend an issued loan within its policy", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Loans" + ], + "requestBody": { + "content": { + "type": { + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "Unspecified request body" + } + }, + "properties": { + "schema": {} + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoanRenewal" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/member-card": { + "get": { + "operationId": "api_v1_member_makerspaces_member_card_retrieve", + "summary": "Read my member card", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OwnMemberCard" + } + } + }, + "description": "" + }, + "400": { + "description": "Membership module disabled or invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "An active membership is required." + }, + "404": { + "description": "No card has been issued to this membership." + } + } + }, + "patch": { + "operationId": "api_v1_member_makerspaces_member_card_partial_update", + "summary": "Set the name printed on my card", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedMemberCardOwnUpdate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedMemberCardOwnUpdate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedMemberCardOwnUpdate" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCard" + } + } + }, + "description": "" + }, + "400": { + "description": "Membership module disabled or invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "An active membership is required." + }, + "404": { + "description": "No card has been issued to this membership." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/member-card/photo": { + "post": { + "operationId": "api_v1_member_makerspaces_member_card_photo_create", + "summary": "Create a card photo upload URL (staging only)", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCardPhotoPresign" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberCardPhotoPresign" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberCardPhotoPresign" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "" + }, + "400": { + "description": "Membership module disabled or invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "An active membership is required." + }, + "404": { + "description": "No card has been issued to this membership." + }, + "503": { + "description": "Storage unavailable." + } + } + }, + "put": { + "operationId": "api_v1_member_makerspaces_member_card_photo_update", + "summary": "Attach an uploaded card photo (requires consent)", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCardPhotoFinalize" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberCardPhotoFinalize" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberCardPhotoFinalize" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCard" + } + } + }, + "description": "" + }, + "400": { + "description": "Membership module disabled or invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "An active membership is required." + }, + "404": { + "description": "No card has been issued to this membership." + } + } + }, + "delete": { + "operationId": "api_v1_member_makerspaces_member_card_photo_destroy", + "summary": "Remove my card photo", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberCard" + } + } + }, + "description": "" + }, + "400": { + "description": "Membership module disabled or invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "An active membership is required." + }, + "404": { + "description": "No card has been issued to this membership." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/member-card/preview.pdf": { + "get": { + "operationId": "api_v1_member_makerspaces_member_card_preview.pdf_retrieve", + "summary": "Preview my card as a watermarked PDF", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { + "description": "Membership module disabled or invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "description": "An active membership is required." + }, + "404": { + "description": "No card has been issued to this membership." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/my-usage": { + "get": { + "operationId": "api_v1_member_makerspaces_my_usage_retrieve", + "description": "Non-payment usage requires an active membership and the membership module, like member activity. Otherwise eligible callers receive payments only. Payment totals follow member_may_see_own_charges and remain visible with membership and payment rail modules disabled. Revoked memberships are refused. All data is filtered by the authenticated user; no member id is accepted. Loan counts represent handovers, deduplicating direct loans' linked workflow requests; monthly returns count ReturnEvents by event time, including partial returns. Legacy direct returns without events use returned_at; quantities are not inferred. Machine hours are attributed MachineUsageEntry hours only; unknown historical attribution is excluded. Booking counts exclude cancelled reservations.", + "summary": "Retrieve only the caller's personal usage charts", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member activity" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberUsage" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "No active membership or own charge visibility." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Makerspace not found or not servable." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/payment-documents": { + "get": { + "operationId": "api_v1_member_makerspaces_payment_documents_retrieve", + "summary": "List scoped payment documents", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Payments" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DocumentPage" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/payment-documents/{document_id}/download-url": { + "get": { + "operationId": "api_v1_member_makerspaces_payment_documents_download_url_retrieve", + "summary": "Get a user-bound five-minute PDF download URL", + "parameters": [ + { + "in": "path", + "name": "document_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Payments" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DownloadGrant" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/payments": { + "get": { + "operationId": "api_v1_member_makerspaces_payments_list", + "description": "Active memberships and account-only charge owners may read their own history. A revoked member reapplying through an approved admission may read only that application's pending admission charge; previous membership debt stays refused.", + "summary": "List the caller's payment history", + "parameters": [ + { + "in": "query", + "name": "due_state", + "schema": { + "type": "string", + "enum": [ + "due_today", + "overdue", + "upcoming" + ] + } + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Payments" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberPayment" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaymentFilterError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "No active membership and no charge here." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/payments/{payment_id}/checkout": { + "post": { + "operationId": "api_v1_member_makerspaces_payments_checkout_create", + "summary": "Generate a Checkout link for the caller's pending payment", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "payment_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Payments" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CheckoutUrl" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "No pending charge of yours here." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "`payment_rail_conflict`: the charge uses the native rail." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "`payments_unavailable`: no live online rail for this charge." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/payments/{payment_id}/mobile-intent": { + "post": { + "operationId": "api_v1_member_makerspaces_payments_mobile_intent_create", + "description": "Requires an attested device token and ownership of a pending charge in the selected makerspace. Returns vendor=stripe PaymentSheet parameters or vendor=razorpay native Order parameters. An existing hosted checkout returns payment_rail_conflict (409); unavailable payments return 503.", + "summary": "Create or retrieve a native mobile payment intent", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "payment_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Payments" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MobilePaymentIntentResponse" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/profile": { + "get": { + "operationId": "api_v1_member_makerspaces_profile_retrieve", + "summary": "Retrieve the caller's own profile", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileRead" + } + } + }, + "description": "" + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "An active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + } + } + }, + "put": { + "operationId": "api_v1_member_makerspaces_profile_update", + "summary": "Update the caller's own profile", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileWrite" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ProfileWrite" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ProfileWrite" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileRead" + } + } + }, + "description": "" + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "An active membership is required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/profile/image": { + "post": { + "operationId": "api_v1_member_makerspaces_profile_image_create", + "summary": "Create a profile image upload URL", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileImageUploadRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ProfileImageUploadRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ProfileImageUploadRequest" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicImageUploadResponse" + } + } + }, + "description": "" + }, + "400": { + "description": "Invalid image upload request." + }, + "403": { + "description": "An active membership is required." + }, + "429": { + "description": "Too many image upload requests." + }, + "503": { + "description": "Public image storage is unavailable." + } + } + }, + "put": { + "operationId": "api_v1_member_makerspaces_profile_image_update", + "summary": "Attach an uploaded image to the profile or one of its projects", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileImageAttachRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ProfileImageAttachRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ProfileImageAttachRequest" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileRead" + } + } + }, + "description": "" + }, + "400": { + "description": "Invalid image object key or size." + }, + "403": { + "description": "An active membership is required." + }, + "503": { + "description": "Public image storage is unavailable." + } + } + }, + "delete": { + "operationId": "api_v1_member_makerspaces_profile_image_destroy", + "summary": "Clear a profile or project image", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member profile" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProfileRead" + } + } + }, + "description": "" + }, + "400": { + "description": "Unknown project." + }, + "403": { + "description": "An active membership is required." + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/referrals": { + "post": { + "operationId": "api_v1_member_makerspaces_referrals_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReferralCreate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ReferralCreate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ReferralCreate" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReferralOutcome" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/verification": { + "post": { + "operationId": "api_v1_member_makerspaces_verification_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberVerificationRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberVerificationRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberVerificationRequest" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberVerificationResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/waiver": { + "get": { + "operationId": "api_v1_member_makerspaces_waiver_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberWaiverResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/member/makerspaces/{makerspace_id}/waiver/accept": { + "post": { + "operationId": "api_v1_member_makerspaces_waiver_accept_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WaiverAcceptResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/memberships/{id}/accept-invitation": { + "post": { + "operationId": "api_v1_memberships_accept_invitation_create", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdmissionAnswers" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/AdmissionAnswers" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/AdmissionAnswers" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvitationClaimOutcome" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/memberships/invitations": { + "get": { + "operationId": "api_v1_memberships_invitations_retrieve", + "tags": [ + "Memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvitationList" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/memberships/invitations/{id}/claim": { + "post": { + "operationId": "api_v1_memberships_invitations_claim_create", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdmissionAnswers" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/AdmissionAnswers" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/AdmissionAnswers" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvitationClaimOutcome" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/memberships/me": { + "get": { + "operationId": "api_v1_memberships_me_retrieve", + "tags": [ + "Memberships" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MyMemberships" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/metrics/": { + "get": { + "operationId": "api_v1_metrics_retrieve", + "description": "Prometheus text exposition of queue depth, request states, notification delivery outcomes, storage accounting and evidence retention. Requires the deployment's METRICS_TOKEN as a bearer token; 404 when no token is configured.", + "summary": "Prometheus metrics", + "tags": [ + "Health" + ], + "security": [ + {} + ], + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "" + }, + "401": { + "description": "Missing or wrong METRICS_TOKEN." + }, + "404": { + "description": "METRICS_TOKEN is not set." + } + } + } + }, + "/api/v1/notifications/announcements/{makerspace_id}": { + "get": { + "operationId": "api_v1_notifications_announcements_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "Member communication" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InboxPage" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/notifications/broadcasts/{makerspace_id}": { + "get": { + "operationId": "api_v1_notifications_broadcasts_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member communication" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BroadcastOptions" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + }, + "post": { + "operationId": "api_v1_notifications_broadcasts_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Member communication" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BroadcastInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BroadcastInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BroadcastInput" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BroadcastResult" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/notifications/inbox/{makerspace_id}": { + "get": { + "operationId": "api_v1_notifications_inbox_retrieve", + "parameters": [ + { + "in": "query", + "name": "feature", + "schema": { + "type": "string", + "enum": [ + "api_access", + "bookings", + "certifications", + "devices", + "events", + "hardware_requests", + "maintenance", + "members", + "payments", + "printing", + "stock" + ] + } + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "unread", + "schema": { + "type": "boolean" + } + } + ], + "tags": [ + "Personal notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InboxPage" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/notifications/inbox/{makerspace_id}/{id}/read": { + "post": { + "operationId": "api_v1_notifications_inbox_read_create", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Personal notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Inbox" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/notifications/inbox/{makerspace_id}/read-all": { + "post": { + "operationId": "api_v1_notifications_inbox_read_all_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Personal notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReadAll" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/notifications/inbox/{makerspace_id}/unread-count": { + "get": { + "operationId": "api_v1_notifications_inbox_unread_count_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Personal notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Count" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + } + }, + "/api/v1/notifications/makerspace/{makerspace_id}": { + "get": { + "operationId": "api_v1_notifications_makerspace_list", + "summary": "List makerspace notifications", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "unread", + "schema": { + "type": "boolean" + }, + "description": "When true, return only unread notifications." + } + ], + "tags": [ + "Notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedNotificationList" + } + } + }, + "description": "" + }, + "400": { + "description": "Notifications module is disabled." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/notifications/makerspace/{makerspace_id}/{id}/read": { + "post": { + "operationId": "api_v1_notifications_makerspace_read_create", + "summary": "Mark a makerspace notification read", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Notification" + } + } + }, + "description": "" + }, + "400": { + "description": "Notifications module is disabled." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/notifications/makerspace/{makerspace_id}/read-all": { + "post": { + "operationId": "api_v1_notifications_makerspace_read_all_create", + "summary": "Mark all makerspace notifications read", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationMarkAllRead" + } + } + }, + "description": "" + }, + "400": { + "description": "Notifications module is disabled." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/notifications/makerspace/{makerspace_id}/unread-count": { + "get": { + "operationId": "api_v1_notifications_makerspace_unread_count_retrieve", + "summary": "Get unread makerspace notification count", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotificationUnreadCount" + } + } + }, + "description": "" + }, + "400": { + "description": "Notifications module is disabled." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/notifications/preferences/{makerspace_id}": { + "get": { + "operationId": "api_v1_notifications_preferences_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Personal notifications" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreferenceResponse" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + }, + "patch": { + "operationId": "api_v1_notifications_preferences_partial_update", + "parameters": [ + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Personal notifications" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedPreferenceWrite" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedPreferenceWrite" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedPreferenceWrite" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreferenceResponse" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/notifications/unsubscribe/{token}": { + "post": { + "operationId": "api_v1_notifications_unsubscribe_create", + "parameters": [ + { + "in": "path", + "name": "token", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Personal notifications" + ], + "requestBody": { + "content": { + "type": { + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "Unspecified request body" + } + }, + "required": { + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "Unspecified request body" + } + }, + "properties": { + "schema": { + "List-Unsubscribe": { + "type": "string", + "enum": [ + "One-Click" + ] + } + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnsubscribeResult" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "415": { + "description": "Unsupported media type for the request body." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/payment-documents/{document_id}/download": { + "get": { + "operationId": "api_v1_payment_documents_download_retrieve", + "summary": "Download an issued payment document PDF", + "parameters": [ + { + "in": "path", + "name": "document_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "token", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Payments" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/payments/connect/callback": { + "get": { + "operationId": "api_v1_payments_connect_callback_retrieve", + "summary": "Complete Stripe Connect onboarding", + "parameters": [ + { + "in": "query", + "name": "code", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "error", + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "state", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Payments" + ], + "responses": { + "302": { + "description": "Redirect to trusted staff settings." + } + } + } + }, + "/api/v1/procurement/makerspace/{makerspace_id}/to-buy": { + "get": { + "operationId": "api_v1_procurement_makerspace_to_buy_list", + "summary": "List to-buy items for a makerspace", + "parameters": [ + { + "in": "query", + "name": "limit", + "schema": { + "type": "integer" + } + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "approved", + "cancelled", + "ordered", + "received", + "requested" + ] + }, + "description": "Filter by procurement item status." + } + ], + "tags": [ + "Procurement" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToBuyItem" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + }, + "post": { + "operationId": "api_v1_procurement_makerspace_to_buy_create", + "summary": "Add a to-buy item", + "parameters": [ + { + "in": "query", + "name": "kind", + "schema": { + "type": "string", + "enum": [ + "hardware", + "printing" + ] + }, + "description": "Stream to add to. Honored only for makerspace admins/superadmin; other roles are auto-tagged by role." + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyItem" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ToBuyItem" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ToBuyItem" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyItem" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/makerspace/{makerspace_id}/to-buy/export": { + "get": { + "operationId": "api_v1_procurement_makerspace_to_buy_export_retrieve", + "summary": "Export to-buy items as CSV or XLSX", + "parameters": [ + { + "in": "query", + "name": "format", + "schema": { + "type": "string", + "enum": [ + "csv", + "xlsx" + ] + } + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "query", + "name": "status", + "schema": { + "type": "string", + "enum": [ + "approved", + "cancelled", + "ordered", + "received", + "requested" + ] + }, + "description": "Filter by procurement item status." + } + ], + "tags": [ + "Procurement" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "text/csv": { + "schema": { + "type": "string" + } + }, + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/makerspace/{makerspace_id}/to-buy/machine-types": { + "get": { + "operationId": "api_v1_procurement_makerspace_to_buy_machine_types_retrieve", + "summary": "List machine types available for a new to-buy item", + "parameters": [ + { + "in": "query", + "name": "kind", + "schema": { + "type": "string", + "enum": [ + "hardware", + "printing" + ] + }, + "description": "Stream to add to. Honored only for makerspace admins/superadmin; other roles are auto-tagged by role." + }, + { + "in": "path", + "name": "makerspace_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyMachineTypeOptions" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/to-buy/{id}": { + "get": { + "operationId": "api_v1_procurement_to_buy_retrieve", + "summary": "Retrieve a to-buy item", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyItem" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + }, + "patch": { + "operationId": "api_v1_procurement_to_buy_partial_update", + "summary": "Update a to-buy item", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PatchedToBuyItem" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PatchedToBuyItem" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PatchedToBuyItem" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyItem" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + }, + "delete": { + "operationId": "api_v1_procurement_to_buy_destroy", + "summary": "Delete a to-buy item", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/to-buy/{id}/move-to-inventory": { + "post": { + "operationId": "api_v1_procurement_to_buy_move_to_inventory_create", + "summary": "Move a received hardware to-buy item into inventory", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MoveToInventoryRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MoveToInventoryRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MoveToInventoryRequest" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyItem" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/to-buy/{id}/move-to-printing": { + "post": { + "operationId": "api_v1_procurement_to_buy_move_to_printing_create", + "summary": "Move a received printing to-buy item into printing assets", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MoveToPrintingRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MoveToPrintingRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MoveToPrintingRequest" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyItem" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/to-buy/{id}/receipts": { + "get": { + "operationId": "api_v1_procurement_to_buy_receipts_list", + "summary": "List procurement receipts for a to-buy item", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToBuyReceipt" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + }, + "post": { + "operationId": "api_v1_procurement_to_buy_receipts_create", + "summary": "Finalize an uploaded procurement receipt", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceiptFinalize" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceiptFinalize" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceiptFinalize" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceipt" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "503": { + "description": "Receipt storage is unavailable." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/to-buy/{id}/receipts/presign": { + "post": { + "operationId": "api_v1_procurement_to_buy_receipts_presign_create", + "summary": "Create a procurement receipt upload URL", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceiptPresign" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceiptPresign" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceiptPresign" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceiptUploadResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "503": { + "description": "Receipt storage is unavailable." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/to-buy/receipts/{id}": { + "delete": { + "operationId": "api_v1_procurement_to_buy_receipts_destroy", + "summary": "Delete a procurement receipt", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/procurement/to-buy/receipts/{id}/url": { + "get": { + "operationId": "api_v1_procurement_to_buy_receipts_url_retrieve", + "summary": "Create a signed procurement receipt view URL", + "parameters": [ + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "Procurement" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToBuyReceiptUrl" + } + } + }, + "description": "" + }, + "503": { + "description": "Receipt storage is unavailable." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + }, + "description": "Invalid request." + }, + "401": { + "description": "Authentication credentials were not provided." + }, + "403": { + "description": "Permission denied." + }, + "404": { + "description": "Not found." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/bookings/calendar/{raw_token}.ics": { + "get": { + "operationId": "api_v1_public_bookings_calendar_.ics_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "raw_token", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "responses": { + "200": { + "content": { + "text/calendar": { + "schema": { + "type": "string" + } + } + }, + "description": "Own reservations as RFC 5545 ICS." + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "429": { + "description": "Rate limit exceeded; retry after the interval in Retry-After." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/event-calendar/{raw_token}.ics": { + "get": { + "operationId": "api_v1_public_event_calendar_.ics_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "raw_token", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public events" + ], + "responses": { + "200": { + "content": { + "text/calendar": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "RFC 5545 calendar (`text/calendar; charset=utf-8`)." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Calendar not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/": { + "get": { + "operationId": "api_v1_public_events_list", + "parameters": [ + { + "in": "query", + "name": "category", + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Full-text search over title, location and description; supports \"phrases\" and -exclusions, tolerates typos in the title." + }, + { + "in": "query", + "name": "tag", + "schema": { + "type": "string" + } + } + ], + "tags": [ + "Public events" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicEvent" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request." + }, + "404": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Event not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/": { + "get": { + "operationId": "api_v1_public_events_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public events" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicEvent" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request." + }, + "404": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Event not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/calendar.ics": { + "get": { + "operationId": "api_v1_public_events_calendar.ics_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public events" + ], + "responses": { + "200": { + "content": { + "text/calendar": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "RFC 5545 calendar (`text/calendar; charset=utf-8`)." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Events module unavailable." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Calendar not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/eligibility/": { + "get": { + "operationId": "api_v1_public_events_eligibility_retrieve", + "description": "Account-specific advisory only, private/no-store. Owned live registrations may renew host consent after signup closes, until the event ends. No membership or eligibility-source identities. Admission rechecks all proofs and current host consent transactionally.", + "summary": "Read private organization audience eligibility", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public events" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventEligibility" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/feedback/": { + "get": { + "operationId": "api_v1_public_events_feedback_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public events" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackForm" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid feedback answers." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required for a certificate." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback form not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback retry conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + }, + "post": { + "operationId": "api_v1_public_events_feedback_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public events" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackSubmission" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/FeedbackSubmission" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/FeedbackSubmission" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackSubmissionResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid feedback answers." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required for a certificate." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback form not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Feedback retry conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/register/": { + "post": { + "operationId": "api_v1_public_events_register_create", + "description": "Invite-only admission accepts a code or a matching verified account email. Code-free unverified admission returns email_verification_required; all normal admission, capacity and waiver rules apply.", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public events" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicEventRegistrationInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicEventRegistrationInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicEventRegistrationInput" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicEventRegistrationResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request." + }, + "404": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Event not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Rate limit exceeded." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "An active authenticated account is required; member audiences also require active membership and the current waiver." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Event state conflict or cancelled paid registration cannot be reused." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/website/": { + "get": { + "operationId": "api_v1_public_events_website_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public event website" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Website" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, hidden, disabled, unreleased or expired website content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage is unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/website/assets/{asset_id}/": { + "get": { + "operationId": "api_v1_public_events_website_assets_retrieve", + "parameters": [ + { + "in": "path", + "name": "asset_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public event website" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "Released WebP image." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, hidden, disabled, unreleased or expired website content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage is unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/website/schedule/": { + "get": { + "operationId": "api_v1_public_events_website_schedule_list", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public event website" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsiteSession" + } + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, hidden, disabled, unreleased or expired website content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage is unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/website/sections/{slug}/": { + "get": { + "operationId": "api_v1_public_events_website_sections_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public event website" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteSection" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, hidden, disabled, unreleased or expired website content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage is unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/website/sessions/{slug}/": { + "get": { + "operationId": "api_v1_public_events_website_sessions_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public event website" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteSession" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, hidden, disabled, unreleased or expired website content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage is unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/events/{public_token}/website/speakers/{slug}/": { + "get": { + "operationId": "api_v1_public_events_website_speakers_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "path", + "name": "slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public event website" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebsiteSection" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, hidden, disabled, unreleased or expired website content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage is unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/inventory/": { + "get": { + "operationId": "api_v1_public_inventory_list", + "description": "List public inventory products for a public makerspace.", + "summary": "List public inventory products", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "query", + "name": "category", + "schema": { + "type": "string" + }, + "description": "Filter public products by category slug." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "description": "Public makerspace code (for example TSEL) or slug.", + "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Full-text search over public name and description; supports \"phrases\" and -exclusions, tolerates typos in the name." + }, + { + "in": "query", + "name": "sort", + "schema": { + "type": "string", + "enum": [ + "most_used", + "name", + "popular" + ] + }, + "description": "Sort public products." + } + ], + "tags": [ + "Public inventory" + ], + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedPublicProductList" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public inventory module or loans feature is disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Invalid authentication credentials." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public API client is not authorized." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Item or public makerspace not found, or hidden by edition." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public read rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/inventory/{id}/": { + "get": { + "operationId": "api_v1_public_inventory_retrieve", + "description": "Read-only public catalogue detail. Private, archived and cross-space items share the same not-found response.", + "summary": "Get public inventory product detail", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public inventory" + ], + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicProduct" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public inventory module or loans feature is disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Invalid authentication credentials." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public API client is not authorized." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Item or public makerspace not found, or hidden by edition." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public read rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/inventory/{id}/watch/": { + "post": { + "operationId": "api_v1_public_inventory_watch_create", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "api" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WatchResponse" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" + } + } + }, + "delete": { + "operationId": "api_v1_public_inventory_watch_destroy", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "api" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/inventory/categories/": { + "get": { + "operationId": "api_v1_public_inventory_categories_list", + "summary": "List public inventory categories", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public inventory" + ], + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicCategory" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public inventory module or loans feature is disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Invalid authentication credentials." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public API client is not authorized." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Item or public makerspace not found, or hidden by edition." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public read rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/invitation-requests": { + "post": { + "operationId": "api_v1_public_invitation_requests_create", + "summary": "Ask a makerspace to invite you", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvitationRequestCreate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/InvitationRequestCreate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/InvitationRequestCreate" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "202": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvitationRequestAck" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "description": "Unknown makerspace." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machine-service-requests": { + "post": { + "operationId": "api_v1_public_machine_service_requests_create", + "summary": "Submit a machine service request as a member", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public machine service" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicMachineServiceSubmit" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicMachineServiceSubmit" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicMachineServiceSubmit" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicMachineServiceSubmitResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid machine service request input." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active membership, waiver acceptance, and presence are required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Makerspace or machine not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Machine service request conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Request rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machine-service/3d-printer/consumable-pools": { + "get": { + "operationId": "api_v1_public_machine_service_3d_printer_consumable_pools_list", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public machine service" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicPrinterPool" + } + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machine-service/3d-printer/queues": { + "get": { + "operationId": "api_v1_public_machine_service_3d_printer_queues_list", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public machine service" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicPrinterQueue" + } + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machine-service/3d-printer/requests": { + "post": { + "operationId": "api_v1_public_machine_service_3d_printer_requests_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public machine service" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterSubmit" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterSubmit" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterSubmit" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterSubmitResponse" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machine-service/3d-printer/uploads": { + "post": { + "operationId": "api_v1_public_machine_service_3d_printer_uploads_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public machine service" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterUpload" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterUpload" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterUpload" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterSubmitResponse" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machine-types/{type_id}/intake": { + "get": { + "operationId": "api_v1_public_machine_types_intake_retrieve", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "type_id", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "tags": [ + "api" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicTypeIntake" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machines": { + "get": { + "operationId": "api_v1_public_machines_list", + "description": "List active machines published by a public makerspace.", + "summary": "List public machines", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "description": "Public makerspace code or slug.", + "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "q", + "schema": { + "type": "string" + }, + "description": "Full-text search over name, location, firmware version and notes; tolerates typos in the name." + } + ], + "tags": [ + "Public machines" + ], + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedPublicMachineList" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "module": { + "type": "string" + } + }, + "required": [ + "module" + ] + } + } + }, + "description": "Machines module disabled." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Public makerspace not found." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid API credentials." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "API client not authorized." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machines/{id}": { + "get": { + "operationId": "api_v1_public_machines_retrieve", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "api" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicMachineDetail" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/machines/details": { + "get": { + "operationId": "api_v1_public_machines_details_list", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "page", + "required": false, + "in": "query", + "description": "A page number within the paginated result set.", + "schema": { + "type": "integer" + } + } + ], + "tags": [ + "api" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedPublicMachineDetailList" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/membership-admission": { + "get": { + "operationId": "api_v1_public_membership_admission_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicAdmission" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/membership-requests": { + "post": { + "operationId": "api_v1_public_membership_requests_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Memberships" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MembershipRequestCreate" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MembershipRequestCreate" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MembershipRequestCreate" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MembershipOutcome" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/": { + "get": { + "operationId": "api_v1_public_my_bookings_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "query", + "name": "page", + "schema": { + "type": "integer" + } + }, + { + "in": "query", + "name": "period", + "schema": { + "enum": [ + "upcoming", + "past" + ], + "type": "string", + "default": "upcoming", + "minLength": 1 + }, + "description": "* `upcoming` - upcoming\n* `past` - past" + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberBookingList" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request or bookings disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign-in required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active account required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Makerspace or own booking not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/calendar.ics": { + "get": { + "operationId": "api_v1_public_my_bookings_calendar.ics_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "text/calendar": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/cancel/": { + "post": { + "operationId": "api_v1_public_my_bookings_cancel_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "requestBody": { + "content": { + "type": { + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "Unspecified request body" + } + }, + "properties": { + "schema": {} + }, + "additionalProperties": { + "schema": { + "type": "object", + "additionalProperties": {}, + "description": "Unspecified request body" + } + } + } + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberBooking" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request or bookings disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign-in required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active account required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Makerspace or own booking not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant frozen for migration." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant write gate unavailable." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Cancellation cutoff or state conflict." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/confirmation/": { + "get": { + "operationId": "api_v1_public_my_bookings_confirmation_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingConfirmation" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/confirmation.png": { + "get": { + "operationId": "api_v1_public_my_bookings_confirmation.png_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/pass.pdf": { + "get": { + "operationId": "api_v1_public_my_bookings_pass.pdf_retrieve", + "description": "Authenticated booking owner only. Confirmed/completed answer-free pass; bookings.passes required. No membership module needed.", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "Private answer-free booking pass. Never contains a bearer QR." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/reschedule/": { + "post": { + "operationId": "api_v1_public_my_bookings_reschedule_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingReschedule" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/BookingReschedule" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/BookingReschedule" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberBooking" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request or bookings disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Sign-in required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active account required." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Makerspace or own booking not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Cutoff, state or slot conflict." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant frozen." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant gate unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/{public_token}/rotate-checkin/": { + "post": { + "operationId": "api_v1_public_my_bookings_rotate_checkin_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingConfirmation" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/my-bookings/calendar/": { + "get": { + "operationId": "api_v1_public_my_bookings_calendar_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedState" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + } + } + }, + "post": { + "operationId": "api_v1_public_my_bookings_calendar_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedIssued" + } + } + }, + "description": "" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + }, + "delete": { + "operationId": "api_v1_public_my_bookings_calendar_destroy", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Member bookings" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "204": { + "description": "No response body" + }, + "400": { + "description": "Validation failed, or the makerspace module this route belongs to is off." + }, + "401": { + "description": "Not signed in." + }, + "403": { + "description": "Signed in, but this action or makerspace is not permitted (or the secret is wrong)." + }, + "404": { + "description": "Not found, not visible to this actor, or the surface is not enabled here." + }, + "423": { + "description": "Makerspace writes are frozen for tenant migration." + }, + "503": { + "description": "A required dependency is unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/presence-sessions": { + "post": { + "operationId": "api_v1_public_presence_sessions_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Presence" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PresenceStart" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PresenceStart" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PresenceStart" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PresenceSession" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Membership permission required." + }, + "404": { + "description": "Makerspace not found." + }, + "429": { + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/presence-sessions/current": { + "get": { + "operationId": "api_v1_public_presence_sessions_current_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Presence" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PresenceCurrent" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Membership permission required." + }, + "404": { + "description": "Makerspace not found." + }, + "429": { + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/presence-sessions/current/end": { + "post": { + "operationId": "api_v1_public_presence_sessions_current_end_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Presence" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PresenceCurrent" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid input." + }, + "401": { + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Membership permission required." + }, + "404": { + "description": "Makerspace not found." + }, + "429": { + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/requests": { + "post": { + "operationId": "api_v1_public_requests_create", + "summary": "Submit public borrow request", + "parameters": [ + { + "in": "header", + "name": "Idempotency-Key", + "schema": { + "type": "string" + }, + "description": "Required for account-less submissions. Reusing a key with the same payload returns the original request; a different payload is rejected." + }, + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public requests" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestSubmit" + }, + "examples": { + "SubmitPublicEquipmentRequest": { + "value": { + "contact_name": "Shaan Shoukath", + "contact_email": "shaans@example.com", + "contact_phone": "+919876543210", + "requested_for": "Electronics workshop diagnostics", + "items": [ + { + "product_id": 42, + "quantity": 2 + } + ] + }, + "summary": "Submit public equipment request" + } + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/RequestSubmit" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RequestSubmit" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequestSubmitResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {}, + "properties": { + "feature": { + "type": "string" + } + } + } + } + }, + "description": "Invalid submission or loans feature disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Too many requests." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant frozen for migration." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/spaces/": { + "get": { + "operationId": "api_v1_public_spaces_list", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public bookings" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicBookableSpace" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request." + }, + "404": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Space not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/spaces/{public_token}/": { + "get": { + "operationId": "api_v1_public_spaces_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public booking website" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicBookingResource" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, private, inactive, disabled or unreleased content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/spaces/{public_token}/availability/": { + "get": { + "operationId": "api_v1_public_spaces_availability_retrieve", + "parameters": [ + { + "in": "query", + "name": "ends_at", + "schema": { + "type": "string", + "format": "date-time" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + }, + { + "in": "query", + "name": "starts_at", + "schema": { + "type": "string", + "format": "date-time" + }, + "required": true + } + ], + "tags": [ + "Public bookings" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicSpaceAvailability" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request." + }, + "404": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Space not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/spaces/{public_token}/book/": { + "post": { + "operationId": "api_v1_public_spaces_book_create", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public bookings" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicBookingInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicBookingInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicBookingInput" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "type": "string", + "enum": [ + "pending", + "confirmed" + ] + } + }, + "required": [ + "status" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "type": "string", + "enum": [ + "pending", + "confirmed" + ] + }, + "public_token": { + "type": "string", + "format": "uuid" + }, + "confirmation_available": { + "type": "boolean" + } + }, + "required": [ + "status", + "public_token", + "confirmation_available" + ] + } + ] + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Invalid request." + }, + "404": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Space not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": {} + } + } + }, + "description": "Rate limit exceeded." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication is required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Active account and any existing membership waiver acceptance are required." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Booking conflict." + }, + "423": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant frozen for migration." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Tenant write gate unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/spaces/{public_token}/website/": { + "get": { + "operationId": "api_v1_public_spaces_website_retrieve", + "parameters": [ + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public booking website" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingWebsite" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, private, inactive, disabled or unreleased content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/spaces/{public_token}/website/assets/{asset_id}/": { + "get": { + "operationId": "api_v1_public_spaces_website_assets_retrieve", + "parameters": [ + { + "in": "path", + "name": "asset_id", + "schema": { + "type": "integer" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public booking website" + ], + "responses": { + "200": { + "content": { + "image/webp": { + "schema": { + "type": "string", + "format": "binary" + } + } + }, + "description": "Live-gated released WebP image." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown, private, inactive, disabled or unreleased content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Image storage unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/stats/": { + "get": { + "operationId": "api_v1_public_stats_retrieve", + "description": "Get public activity stats for a public makerspace.", + "summary": "Get public makerspace stats", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "description": "Public makerspace code (for example TSEL) or slug.", + "required": true + } + ], + "tags": [ + "Public inventory" + ], + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicStats" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/tools/checklist": { + "post": { + "operationId": "api_v1_public_tools_checklist_create", + "summary": "Preview kit components for a scanned self-checkout tool", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public requests" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemberKitPreviewInput" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/MemberKitPreviewInput" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/MemberKitPreviewInput" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KitPreview" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/{makerspace_slug}/tools/checkout": { + "post": { + "operationId": "api_v1_public_tools_checkout_create", + "summary": "Check out a public tool by QR", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public requests" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicToolCheckout" + }, + "examples": { + "PublicQRToolCheckout": { + "value": { + "payload": "BOX-ABC123", + "requester_name": "Shaan Shoukath", + "contact_email": "shaans@example.com", + "contact_phone": "+919876543210", + "evidence_id": 122, + "remark": "Borrowing for electronics workshop diagnostics." + }, + "summary": "Public QR tool checkout" + } + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicToolCheckout" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicToolCheckout" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicToolLoan" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Too many requests." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/tools/evidence-url": { + "post": { + "operationId": "api_v1_public_tools_evidence_url_create", + "summary": "Create a public self-checkout evidence upload URL", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public requests" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicToolEvidenceUrlRequest" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicToolEvidenceUrlRequest" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicToolEvidenceUrlRequest" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "201": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EvidenceUrlResponse" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Too many requests." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." + } + } + } + }, + "/api/v1/public/{makerspace_slug}/tools/return": { + "post": { + "operationId": "api_v1_public_tools_return_create", + "summary": "Return a public tool by QR", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public requests" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicToolScan" + }, + "examples": { + "PublicQRToolReturnScan": { + "value": { + "identifier": "shaans@example.com", + "payload": "BOX-ABC123", + "evidence_id": 123, + "remark": "Returned to the electronics shelf in good condition." + }, + "summary": "Public QR tool return scan" + } + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/PublicToolScan" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/PublicToolScan" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicToolLoan" + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid request." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + }, + "409": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Workflow conflict." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Too many requests." + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Service unavailable." + } + } + } + }, + "/api/v1/public/machine-service/3d-printer/requests/{public_token}/status": { + "get": { + "operationId": "api_v1_public_machine_service_3d_printer_requests_status_retrieve", + "parameters": [ + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public machine service" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicPrinterStatus" + } + } + }, + "description": "" + } + } + } + }, + "/api/v1/public/makerspaces/": { + "get": { + "operationId": "api_v1_public_makerspaces_list", + "description": "List makerspaces that have a public site enabled.", + "summary": "List public makerspaces", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + } + ], + "tags": [ + "Public inventory" + ], + "security": [ + { + "jwtAuth": [] + }, + {} + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicMakerspace" + } + } + } + }, + "description": "" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public inventory module or loans feature is disabled." + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Invalid authentication credentials." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public API client is not authorized." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Item or public makerspace not found, or hidden by edition." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicInventoryError" + } + } + }, + "description": "Public read rate limit exceeded." + } + } + } + }, + "/api/v1/public/organizations/{slug}/": { + "get": { + "operationId": "api_v1_public_organizations_retrieve", + "summary": "Retrieve a public organization profile", + "parameters": [ + { + "in": "path", + "name": "slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public organizations" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicOrganization" + } + } + }, + "description": "" + }, + "404": { + "description": "Organization not found." + }, + "429": { + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/organizations/{slug}/events/": { + "get": { + "operationId": "api_v1_public_organizations_events_retrieve", + "summary": "List public events organized across makerspaces", + "parameters": [ + { + "in": "path", + "name": "slug", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Public organizations" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicOrganizationEventList" + } + } + }, + "description": "" + }, + "404": { + "description": "Organization not found." + }, + "429": { + "description": "Rate limit exceeded." + } + } + } + }, + "/api/v1/public/requests/{public_token}/status": { + "get": { + "operationId": "api_v1_public_requests_status_retrieve", + "summary": "Get request status by public token", + "parameters": [ + { + "in": "header", + "name": "X-Nonce", + "schema": { + "type": "string" + }, + "description": "Unique, unpredictable nonce for HMAC-authenticated server API clients (1-128 characters: letters, digits, `.`, `_`, `~`, or `-`). Include it between `X-Timestamp` and the raw body in the signed bytes: `METHOD\\nFULL_PATH\\nTIMESTAMP\\nNONCE\\nBODY`. It is optional only for publishable-key/browser authentication and during the temporary legacy rollout while `APICLIENT_REQUIRE_NONCE` is disabled." + }, + { + "in": "header", + "name": "X-Publishable-Key", + "schema": { + "type": "string" + }, + "description": "Public API key for a makerspace public client. Required when API_CLIENT_AUTH_REQUIRED is enabled." + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public requests" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicRequestStatus" + }, + "examples": { + "PublicRequestStatus": { + "value": { + "public_token": "4f2b93e1-6ef4-41c2-8407-7f26bb3b2d8f", + "requested_for": "Electronics workshop diagnostics", + "status": "pending_approval", + "rejection_reason": "", + "created_at": "2026-06-11T10:30:00Z", + "items": [ + { + "product_name": "Soldering Iron", + "requested_quantity": 2 + } + ] + }, + "summary": "Public request status" + } + } + } + }, + "description": "" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Authentication required." + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Permission denied." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Not found." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Too many requests." + } + } + } + }, + "/api/v1/recovery": { + "get": { + "operationId": "api_v1_recovery_retrieve", + "summary": "Get deployment quarantine and residual-risk state", + "tags": [ + "Backup recovery" + ], + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RecoveryState" + } + } + }, + "description": "" + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." + } + } + }, + "post": { + "operationId": "api_v1_recovery_create", + "summary": "Acknowledge residual risk and lift quarantine", + "tags": [ + "Backup recovery" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RecoveryAcknowledge" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/RecoveryAcknowledge" + } + }, + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/RecoveryAcknowledge" + } + } + }, + "required": true + }, + "security": [ + { + "jwtAuth": [] + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RecoveryState" + } + } + }, + "description": "" + }, + "400": { + "description": "The request is invalid for the current lifecycle state." + }, + "401": { + "description": "Authentication is required." + }, + "403": { + "description": "The authenticated actor is not authorized." + } + } + } + }, + "/api/v1/webhooks/razorpay/{public_code}": { + "post": { + "operationId": "api_v1_webhooks_razorpay_create", + "description": "Verifies the X-Razorpay-Signature HMAC over request.body using the addressed makerspace's webhook secret before applying an idempotent event.", + "summary": "Receive a makerspace Razorpay webhook", + "parameters": [ + { + "in": "path", + "name": "public_code", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Payments" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Verified event acknowledged (acted on or deliberately ignored)." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Invalid signature, payload, or configuration." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Makerspace was not found." + } + } + } + }, + "/api/v1/webhooks/stripe/{public_code}": { + "post": { + "operationId": "api_v1_webhooks_stripe_create", + "description": "Verifies the Stripe signature using request.body and the addressed makerspace's webhook secret before applying an idempotent event.", + "summary": "Receive a makerspace Stripe webhook", + "parameters": [ + { + "in": "path", + "name": "public_code", + "schema": { + "type": "string" + }, + "required": true + } + ], + "tags": [ + "Payments" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Verified event acknowledged (acted on or deliberately ignored)." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Invalid signature, payload, or configuration." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Makerspace was not found." + } + } + } + }, + "/api/v1/webhooks/stripe/connect": { + "post": { + "operationId": "api_v1_webhooks_stripe_connect_create", + "summary": "Receive a platform Stripe Connect webhook", + "tags": [ + "Payments" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Verified event acknowledged (acted on or deliberately ignored)." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Invalid signature, payload, or configuration." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WebhookResponse" + } + } + }, + "description": "Makerspace was not found." + } + } + } + }, + "/embed/{makerspace_slug}/{kind}/{public_token}/": { + "get": { + "operationId": "embed_retrieve", + "parameters": [ + { + "in": "path", + "name": "kind", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "makerspace_slug", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public websites" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "description": "Self-contained HTML with exact framing CSP." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid discovery filters." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown or unavailable public content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/robots.txt": { + "get": { + "operationId": "robots.txt_retrieve", + "tags": [ + "Public websites" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "description": "Public crawler policy and origin sitemap." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid discovery filters." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown or unavailable public content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/share/{kind}/{public_token}/": { + "get": { + "operationId": "share_retrieve", + "parameters": [ + { + "in": "path", + "name": "kind", + "schema": { + "type": "string" + }, + "required": true + }, + { + "in": "path", + "name": "public_token", + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true + } + ], + "tags": [ + "Public websites" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "description": "Noindex HTML with public OG/Twitter metadata; server cache 600s, live-gated." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid discovery filters." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown or unavailable public content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + }, + "/sitemap.xml": { + "get": { + "operationId": "sitemap.xml_retrieve", + "tags": [ + "Public websites" + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + }, + "description": "Origin-scoped XML sitemap of live public subjects." + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Invalid discovery filters." + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Unknown or unavailable public content." + }, + "429": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HardwareRequestError" + } + } + }, + "description": "Rate limit exceeded." + } + } + } + } + }, + "components": { + "schemas": { + "AcceptQuantity": { + "type": "object", + "properties": { + "item_id": { + "type": "integer" + }, + "quantity": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "item_id", + "quantity" + ] + }, + "AcceptRequest": { + "type": "object", + "properties": { + "accepted_quantities": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AcceptQuantity" + } + } + } + }, + "AccessLevelEnum": { + "enum": [ + "operate", + "manage", + "full" + ], + "type": "string", + "description": "* `operate` - Operate\n* `manage` - Manage\n* `full` - Full" + }, + "AccessStatusEnum": { + "enum": [ + "active", + "restricted", + "suspended" + ], + "type": "string", + "description": "* `active` - Active\n* `restricted` - Restricted\n* `suspended` - Suspended" + }, + "AccountDeletionBlocker": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "makerspace_id": { + "type": "integer", + "nullable": true + }, + "makerspace_slug": { + "type": "string", + "nullable": true + }, + "makerspace_member_url": { + "type": "string", + "nullable": true + }, + "organization_url": { + "type": "string", + "nullable": true + }, + "organization_id": { + "type": "integer", + "nullable": true + }, + "count": { + "type": "integer", + "minimum": 1 + }, + "detail": { + "type": "string" + } + }, + "required": [ + "code", + "count", + "detail", + "makerspace_id", + "makerspace_member_url", + "makerspace_slug", + "organization_id", + "organization_url" + ] + }, + "AccountDeletionForbidden": { + "oneOf": [ + { + "$ref": "#/components/schemas/HardwareRequestError" + }, + { + "$ref": "#/components/schemas/AccountDeletionRefused" + } + ] + }, + "AccountDeletionPreview": { + "type": "object", + "properties": { + "deletable": { + "type": "boolean" + }, + "restorable": { + "type": "boolean" + }, + "restore_reason": { + "type": "string" + }, + "blockers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AccountDeletionBlocker" + } + }, + "reauth": { + "$ref": "#/components/schemas/AccountDeletionReauth" + } + }, + "required": [ + "blockers", + "deletable", + "reauth", + "restorable", + "restore_reason" + ] + }, + "AccountDeletionReauth": { + "type": "object", + "properties": { + "required": { + "type": "boolean" + }, + "methods": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "methods", + "required" + ] + }, + "AccountDeletionRefused": { + "type": "object", + "properties": { + "blockers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AccountDeletionBlocker" + } + } + }, + "required": [ + "blockers" + ] + }, + "AccountDeletionRequest": { + "type": "object", + "properties": { + "confirm": { + "$ref": "#/components/schemas/ConfirmEnum" + } + }, + "required": [ + "confirm" + ] + }, + "AccountDeletionResult": { + "type": "object", + "properties": { + "deleted_at": { + "type": "string", + "format": "date-time" + }, + "purge_after": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "deleted_at", + "purge_after" + ] + }, + "AccountFailure": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "AccountProfile": { + "type": "object", + "properties": { + "display_name": { + "type": "string", + "maxLength": 200 + }, + "first_name": { + "type": "string", + "maxLength": 150 + }, + "last_name": { + "type": "string", + "maxLength": 150 + }, + "phone": { + "type": "string", + "maxLength": 32 + } + } + }, + "AccountRestoreEmail": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "maxLength": 254 + }, + "token": { + "type": "string", + "writeOnly": true, + "maxLength": 512 + }, + "new_password": { + "type": "string", + "writeOnly": true, + "maxLength": 128 + } + }, + "required": [ + "email", + "new_password", + "token" + ] + }, + "AccountRestorePhone": { + "type": "object", + "properties": { + "phone": { + "type": "string", + "maxLength": 32 + }, + "delivery": { + "allOf": [ + { + "$ref": "#/components/schemas/DeliveryEnum" + } + ], + "default": "web" + }, + "client_platform": { + "allOf": [ + { + "$ref": "#/components/schemas/ClientPlatformEnum" + } + ], + "default": "web" + }, + "challenge": { + "type": "string", + "writeOnly": true, + "maxLength": 512 + }, + "code": { + "type": "string", + "maxLength": 6, + "minLength": 6 + }, + "attestation": { + "allOf": [ + { + "$ref": "#/components/schemas/DeviceAttestationPayload" + } + ], + "writeOnly": true + }, + "new_password": { + "type": "string", + "writeOnly": true, + "maxLength": 128 + } + }, + "required": [ + "code", + "new_password", + "phone" + ] + }, + "AccountRestoreResult": { + "type": "object", + "properties": { + "restored": { + "type": "boolean" + }, + "detail": { + "type": "string" + } + }, + "required": [ + "detail", + "restored" + ] + }, + "AccountRestoreSocialComplete": { + "type": "object", + "properties": { + "state": { + "type": "string", + "writeOnly": true, + "maxLength": 512 + }, + "nonce": { + "type": "string", + "writeOnly": true, + "maxLength": 512 + }, + "id_token": { + "type": "string", + "writeOnly": true, + "default": "", + "maxLength": 16384 + }, + "code": { + "type": "string", + "writeOnly": true, + "default": "", + "maxLength": 4096 + }, + "challenge": { + "type": "string", + "writeOnly": true, + "default": "", + "maxLength": 512 + }, + "attestation": { + "allOf": [ + { + "$ref": "#/components/schemas/DeviceAttestationPayload" + } + ], + "writeOnly": true + } + }, + "required": [ + "nonce", + "state" + ] + }, + "AccountRestoreSocialResult": { + "type": "object", + "properties": { + "authorization_url": { + "type": "string", + "format": "uri" + }, + "state": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "expires_in": { + "type": "integer" + } + }, + "required": [ + "authorization_url", + "expires_in", + "nonce", + "state" + ] + }, + "AccountRestoreSocialStart": { + "type": "object", + "properties": { + "delivery": { + "allOf": [ + { + "$ref": "#/components/schemas/DeliveryEnum" + } + ], + "default": "web" + }, + "client_platform": { + "allOf": [ + { + "$ref": "#/components/schemas/ClientPlatformEnum" + } + ], + "default": "web" + }, + "challenge": { + "type": "string", + "writeOnly": true, + "maxLength": 512 + }, + "username": { + "type": "string", + "maxLength": 150 + }, + "provider": { + "type": "string", + "maxLength": 100 + }, + "redirect_uri": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "default": "", + "maxLength": 1000 + }, + { + "type": "string", + "maxLength": 0 + } + ] + } + }, + "required": [ + "provider", + "username" + ] + }, + "AccountSession": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/AccountSessionKindEnum" + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "last_used_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "current": { + "type": "boolean" + } + }, + "required": [ + "created_at", + "current", + "expires_at", + "id", + "kind", + "last_used_at" + ] + }, + "AccountSessionKindEnum": { + "enum": [ + "web", + "device" + ], + "type": "string", + "description": "* `web` - web\n* `device` - device" + }, + "AccountSessionPage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AccountSession" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "AccountingJournalReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/JournalLine" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "Ack": { + "type": "object", + "properties": { + "lease_token": { + "type": "string", + "maxLength": 128, + "minLength": 32 + } + }, + "required": [ + "lease_token" + ] + }, + "ActiveLoansReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ActiveLoansReportRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "ActiveLoansReportRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "requester": { + "type": "string" + }, + "status": { + "type": "string" + }, + "issued_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "count": { + "type": "integer", + "maximum": 1, + "minimum": 1, + "description": "One loan per detail row; sum to count issued loans in this result." + } + }, + "required": [ + "count", + "id", + "issued_at", + "requester", + "status" + ] + }, + "AddressingStyleEnum": { + "enum": [ + "auto", + "path", + "virtual" + ], + "type": "string", + "description": "* `auto` - Auto\n* `path` - Path\n* `virtual` - Virtual host" + }, + "AdminMembership": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "status": { + "$ref": "#/components/schemas/Status37fEnum" + }, + "user": { + "allOf": [ + { + "$ref": "#/components/schemas/AdminMembershipUser" + } + ], + "readOnly": true + }, + "assigned_role": { + "allOf": [ + { + "$ref": "#/components/schemas/AdminMembershipRole" + } + ], + "nullable": true, + "readOnly": true + }, + "role_ids": { + "type": "array", + "items": { + "type": "integer" + }, + "readOnly": true + }, + "roles": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MembershipRole" + }, + "readOnly": true + }, + "can_refer": { + "type": "boolean" + }, + "can_verify": { + "type": "boolean" + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "activated_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "revocation_reason": { + "type": "string" + }, + "waiver_accepted_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "waiver_version_accepted": { + "type": "string", + "nullable": true, + "maxLength": 64 + }, + "waiver_current": { + "type": "boolean", + "readOnly": true + }, + "waiver_required": { + "type": "boolean", + "readOnly": true + }, + "payment": { + "allOf": [ + { + "$ref": "#/components/schemas/StaffPaymentSummary" + } + ], + "nullable": true, + "readOnly": true + }, + "vouches": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Vouch" + }, + "readOnly": true + } + }, + "required": [ + "assigned_role", + "id", + "payment", + "role_ids", + "roles", + "user", + "vouches", + "waiver_current", + "waiver_required" + ] + }, + "AdminMembershipRole": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "AdminMembershipUser": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "username": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "display_name": { + "type": "string" + } + }, + "required": [ + "display_name", + "email", + "id", + "username" + ] + }, + "AdminRequest": { + "type": "object", + "properties": { + "has_kit_checklist": { + "type": "boolean", + "readOnly": true + }, + "id": { + "type": "integer", + "readOnly": true + }, + "makerspace_id": { + "type": "integer", + "readOnly": true + }, + "requester_username": { + "type": "string", + "readOnly": true + }, + "requester_name": { + "type": "string", + "readOnly": true + }, + "requester_display": { + "type": "string", + "readOnly": true + }, + "requester_contact_email": { + "type": "string", + "format": "email", + "readOnly": true + }, + "requester_contact_phone": { + "type": "string", + "readOnly": true + }, + "requester_contact_verified": { + "type": "boolean", + "readOnly": true + }, + "status": { + "type": "string", + "readOnly": true + }, + "requested_for": { + "type": "string", + "readOnly": true + }, + "rejection_reason": { + "type": "string", + "readOnly": true + }, + "assigned_box_label": { + "type": "string", + "readOnly": true, + "nullable": true + }, + "accepted_by": { + "allOf": [ + { + "$ref": "#/components/schemas/AdminRequestActor" + } + ], + "readOnly": true, + "nullable": true + }, + "issued_by": { + "allOf": [ + { + "$ref": "#/components/schemas/AdminRequestActor" + } + ], + "readOnly": true, + "nullable": true + }, + "accepted_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "issued_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "return_due_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "return_reminder_sent_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "renewal_count": { + "type": "integer", + "readOnly": true + }, + "closed_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "issue_evidence_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "return_evidence_ids": { + "type": "array", + "items": {}, + "readOnly": true + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AdminRequestItem" + }, + "readOnly": true + } + }, + "required": [ + "accepted_at", + "accepted_by", + "assigned_box_label", + "closed_at", + "created_at", + "has_kit_checklist", + "id", + "issue_evidence_id", + "issued_at", + "issued_by", + "items", + "makerspace_id", + "rejection_reason", + "renewal_count", + "requested_for", + "requester_contact_email", + "requester_contact_phone", + "requester_contact_verified", + "requester_display", + "requester_name", + "requester_username", + "return_due_at", + "return_evidence_ids", + "return_reminder_sent_at", + "status", + "updated_at" + ] + }, + "AdminRequestActor": { + "type": "object", + "properties": { + "username": { + "type": "string", + "readOnly": true + }, + "role": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "role", + "username" + ] + }, + "AdminRequestItem": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "product_id": { + "type": "integer", + "readOnly": true + }, + "product_name": { + "type": "string", + "readOnly": true + }, + "storage_location": { + "type": "string", + "readOnly": true + }, + "tracking_mode": { + "type": "string", + "readOnly": true + }, + "requires_asset_qr": { + "type": "boolean", + "readOnly": true + }, + "requested_quantity": { + "type": "integer", + "readOnly": true + }, + "accepted_quantity": { + "type": "integer", + "readOnly": true + }, + "issued_quantity": { + "type": "integer", + "readOnly": true + }, + "returned_quantity": { + "type": "integer", + "readOnly": true + }, + "damaged_quantity": { + "type": "integer", + "readOnly": true + }, + "missing_quantity": { + "type": "integer", + "readOnly": true + }, + "needs_fix_quantity": { + "type": "integer", + "readOnly": true + }, + "issued_assets": { + "type": "array", + "items": {}, + "readOnly": true + } + }, + "required": [ + "accepted_quantity", + "damaged_quantity", + "id", + "issued_assets", + "issued_quantity", + "missing_quantity", + "needs_fix_quantity", + "product_id", + "product_name", + "requested_quantity", + "requires_asset_qr", + "returned_quantity", + "storage_location", + "tracking_mode" + ] + }, + "AdmissionAnswers": { + "type": "object", + "properties": { + "application_answers": { + "nullable": true + } + } + }, + "AdmissionExport": { + "type": "object", + "properties": { + "requests": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AdmissionRequest" + } + } + }, + "required": [ + "requests" + ] + }, + "AdmissionPayment": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "currency": { + "type": "string" + } + }, + "required": [ + "amount", + "currency", + "id", + "status" + ] + }, + "AdmissionRequest": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "state": { + "$ref": "#/components/schemas/State9efEnum" + }, + "application_answers": { + "nullable": true, + "readOnly": true + }, + "vouches": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Vouch" + } + }, + "admission_payment": { + "allOf": [ + { + "$ref": "#/components/schemas/AdmissionPayment" + } + ], + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "required": [ + "admission_payment", + "application_answers", + "id", + "state", + "vouches" + ] + }, + "AdmissionSettings": { + "type": "object", + "properties": { + "configured": { + "type": "boolean", + "readOnly": true + }, + "join_mode": { + "$ref": "#/components/schemas/JoinModeEnum" + }, + "vouches_required": { + "type": "integer", + "maximum": 5, + "minimum": 1 + }, + "voucher_min_tenure_days": { + "type": "integer", + "maximum": 3650, + "minimum": 0 + }, + "voucher_pending_limit": { + "type": "integer", + "maximum": 100, + "minimum": 1 + }, + "vouch_expiry_days": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "vouch_replaces_approval": { + "type": "boolean" + }, + "payment_timing": { + "$ref": "#/components/schemas/PaymentTimingEnum" + }, + "application_schema": { + "nullable": true + }, + "default_plan": { + "type": "integer", + "nullable": true + }, + "joining_fee": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "joining_fee_currency": { + "type": "string", + "pattern": "^[A-Za-z]{3}$" + }, + "rejoin_dues_rule": { + "$ref": "#/components/schemas/RejoinDuesRuleEnum" + }, + "request_expiry_days": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "recipe": { + "allOf": [ + { + "$ref": "#/components/schemas/RecipeEnum" + } + ], + "writeOnly": true + } + }, + "required": [ + "configured" + ] + }, + "AdmissionVouchOutcome": { + "type": "object", + "description": "A voucher receives the outcome, never the applicant's private submission.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "state": { + "$ref": "#/components/schemas/State9efEnum" + } + }, + "required": [ + "id", + "state" + ] + }, + "AgeBucketEnum": { + "enum": [ + "0-30", + "31-60", + "61-90", + "90+" + ], + "type": "string", + "description": "* `0-30` - 0-30\n* `31-60` - 31-60\n* `61-90` - 61-90\n* `90+` - 90+" + }, + "AlignEnum": { + "enum": [ + "left", + "center", + "right" + ], + "type": "string", + "description": "* `left` - left\n* `center` - center\n* `right` - right" + }, + "AllowedFormatsEnum": { + "enum": [ + "csv", + "xlsx", + "pdf" + ], + "type": "string", + "description": "* `csv` - csv\n* `xlsx` - xlsx\n* `pdf` - pdf" + }, + "AnalyticsMakerspace": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "AnalyticsMakerspacePage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AnalyticsMakerspace" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "AnalyticsReportResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/AnalyticsSummary" + }, + { + "$ref": "#/components/schemas/TakenItemsReport" + }, + { + "$ref": "#/components/schemas/ActiveLoansReport" + }, + { + "$ref": "#/components/schemas/ReturnsReport" + }, + { + "$ref": "#/components/schemas/DamagedMissingReport" + }, + { + "$ref": "#/components/schemas/DamagedLostReport" + }, + { + "$ref": "#/components/schemas/QrScansReport" + }, + { + "$ref": "#/components/schemas/MostLentReport" + }, + { + "$ref": "#/components/schemas/TopBorrowersReport" + }, + { + "$ref": "#/components/schemas/RecentlyAddedReport" + }, + { + "$ref": "#/components/schemas/MachineUsageV2Report" + }, + { + "$ref": "#/components/schemas/EventAttendanceReport" + }, + { + "$ref": "#/components/schemas/BookingUtilizationReport" + }, + { + "$ref": "#/components/schemas/MaintenanceActivityV2Report" + }, + { + "$ref": "#/components/schemas/MachineUsageMembersReport" + }, + { + "$ref": "#/components/schemas/MachineUsageHeatmapReport" + }, + { + "$ref": "#/components/schemas/MachineDowntimeReport" + }, + { + "$ref": "#/components/schemas/MachineServiceV2Report" + }, + { + "$ref": "#/components/schemas/ServiceQueueTimesReport" + }, + { + "$ref": "#/components/schemas/PrinterJobsReport" + }, + { + "$ref": "#/components/schemas/MachineErrorsReport" + }, + { + "$ref": "#/components/schemas/WarrantyExpiryReport" + }, + { + "$ref": "#/components/schemas/BookingUtilizationV2Report" + }, + { + "$ref": "#/components/schemas/BookingOccupancyReport" + }, + { + "$ref": "#/components/schemas/BookingLeadTimeReport" + }, + { + "$ref": "#/components/schemas/BookingTopMembersReport" + }, + { + "$ref": "#/components/schemas/EventSignupsReport" + }, + { + "$ref": "#/components/schemas/EventRevenueReport" + }, + { + "$ref": "#/components/schemas/EventFunnelReport" + }, + { + "$ref": "#/components/schemas/EventCheckinTimesReport" + }, + { + "$ref": "#/components/schemas/EventFeedbackScoresReport" + }, + { + "$ref": "#/components/schemas/MemberActivityReport" + }, + { + "$ref": "#/components/schemas/FabLabHealthReport" + }, + { + "$ref": "#/components/schemas/PaymentReconciliationReport" + }, + { + "$ref": "#/components/schemas/PaymentCashBook" + }, + { + "$ref": "#/components/schemas/PaymentsLedgerReport" + }, + { + "$ref": "#/components/schemas/RevenueOverTimeReport" + }, + { + "$ref": "#/components/schemas/CashBookReport" + }, + { + "$ref": "#/components/schemas/AccountingJournalReport" + }, + { + "$ref": "#/components/schemas/GenericAnalyticsReport" + }, + { + "$ref": "#/components/schemas/CommunicationsHealthReport" + }, + { + "$ref": "#/components/schemas/PlatformServicesReport" + }, + { + "$ref": "#/components/schemas/MemberRetentionReport" + }, + { + "$ref": "#/components/schemas/MembershipRevenueReport" + }, + { + "$ref": "#/components/schemas/MembershipGrowthReport" + }, + { + "$ref": "#/components/schemas/PaymentsAgeingReport" + }, + { + "$ref": "#/components/schemas/EventRegistrationReport" + }, + { + "$ref": "#/components/schemas/EventFeedbackReport" + }, + { + "$ref": "#/components/schemas/BookingResourceUtilizationReport" + }, + { + "$ref": "#/components/schemas/BookingReservationReport" + }, + { + "$ref": "#/components/schemas/BookingAnswerReport" + } + ] + }, + "AnalyticsSummary": { + "type": "object", + "properties": { + "products": { + "type": "integer" + }, + "assets": { + "type": "integer" + }, + "active_loans": { + "type": "integer" + }, + "available_quantity": { + "type": "integer" + }, + "issued_quantity": { + "type": "integer" + }, + "damaged_quantity": { + "type": "integer" + }, + "missing_quantity": { + "type": "integer" + }, + "asset_available": { + "type": "integer" + }, + "asset_reserved": { + "type": "integer" + }, + "asset_issued": { + "type": "integer" + }, + "asset_damaged": { + "type": "integer" + }, + "asset_lost": { + "type": "integer" + }, + "asset_maintenance": { + "type": "integer" + }, + "asset_retired": { + "type": "integer" + } + }, + "required": [ + "active_loans", + "asset_available", + "asset_damaged", + "asset_issued", + "asset_lost", + "asset_maintenance", + "asset_reserved", + "asset_retired", + "assets", + "available_quantity", + "damaged_quantity", + "issued_quantity", + "missing_quantity", + "products" + ] + }, + "AnchorEnum": { + "enum": [ + "completion", + "calendar" + ], + "type": "string", + "description": "* `completion` - Completion date\n* `calendar` - Fixed calendar" + }, + "AnchorStatusEnum": { + "enum": [ + "pending", + "not_configured", + "externally_anchored", + "invalid", + "unavailable" + ], + "type": "string", + "description": "* `pending` - Pending\n* `not_configured` - Not configured\n* `externally_anchored` - Externally anchored\n* `invalid` - Invalid\n* `unavailable` - Unavailable" + }, + "ApiClient": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "label": { + "type": "string", + "maxLength": 200 + }, + "client_id": { + "type": "string", + "readOnly": true + }, + "client_type": { + "$ref": "#/components/schemas/ClientTypeEnum" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "rate_limit_tier": { + "$ref": "#/components/schemas/RateLimitTierEnum" + }, + "makerspace": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "public_makerspace_code": { + "type": "string", + "readOnly": true + }, + "allowed_origins": { + "type": "array", + "items": { + "type": "string" + } + }, + "backend_base_url": { + "type": "string", + "readOnly": true + }, + "public_api_base_url": { + "type": "string", + "readOnly": true + }, + "is_active": { + "type": "boolean" + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_seen_ip": { + "type": "string", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "allowed_origins", + "backend_base_url", + "client_id", + "created_at", + "id", + "label", + "last_seen_at", + "last_seen_ip", + "makerspace", + "public_api_base_url", + "public_makerspace_code", + "scopes", + "updated_at" + ] + }, + "ApiClientCreateResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "label": { + "type": "string", + "maxLength": 200 + }, + "client_id": { + "type": "string", + "readOnly": true + }, + "client_secret": { + "type": "string", + "readOnly": true + }, + "client_type": { + "$ref": "#/components/schemas/ClientTypeEnum" + }, + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "rate_limit_tier": { + "$ref": "#/components/schemas/RateLimitTierEnum" + }, + "makerspace": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "public_makerspace_code": { + "type": "string", + "readOnly": true + }, + "allowed_origins": { + "type": "array", + "items": { + "type": "string" + } + }, + "backend_base_url": { + "type": "string", + "readOnly": true + }, + "public_api_base_url": { + "type": "string", + "readOnly": true + }, + "is_active": { + "type": "boolean" + }, + "last_seen_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_seen_ip": { + "type": "string", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "allowed_origins", + "backend_base_url", + "client_id", + "client_secret", + "created_at", + "id", + "label", + "last_seen_at", + "last_seen_ip", + "makerspace", + "public_api_base_url", + "public_makerspace_code", + "scopes", + "updated_at" + ] + }, + "ApiClientScopeCatalogResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApiClientScopeOption" + } + } + }, + "required": [ + "count", + "results" + ] + }, + "ApiClientScopeOption": { + "type": "object", + "properties": { + "value": { + "type": "string" + }, + "label": { + "type": "string" + }, + "description": { + "type": "string" + }, + "group": { + "type": "string" + }, + "grantable": { + "type": "boolean" + }, + "lock_reason": { + "type": "string", + "nullable": true + } + }, + "required": [ + "description", + "grantable", + "group", + "label", + "lock_reason", + "value" + ] + }, + "ApiIntegrationSettings": { + "type": "object", + "properties": { + "public_code": { + "type": "string", + "readOnly": true + }, + "public_api_key": { + "type": "string", + "readOnly": true + }, + "cors_allowed_origins": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "telegram_group_chat_id": { + "type": "string", + "maxLength": 64 + }, + "telegram_bot_token": { + "type": "string", + "writeOnly": true + }, + "telegram_bot_token_set": { + "type": "boolean", + "readOnly": true + }, + "smtp_host": { + "type": "string", + "maxLength": 200 + }, + "smtp_port": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "smtp_username": { + "type": "string", + "maxLength": 200 + }, + "smtp_password": { + "type": "string", + "writeOnly": true + }, + "smtp_password_set": { + "type": "boolean", + "readOnly": true + }, + "smtp_use_tls": { + "type": "boolean" + }, + "smtp_use_ssl": { + "type": "boolean" + }, + "smtp_from_email": { + "oneOf": [ + { + "type": "string", + "format": "email", + "maxLength": 254 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "slack_webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2048 + }, + "slack_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "mattermost_webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2048 + }, + "mattermost_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "discord_webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2048 + }, + "discord_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "default_loan_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + } + }, + "required": [ + "cors_allowed_origins", + "discord_webhook_url_set", + "mattermost_webhook_url_set", + "public_api_key", + "public_code", + "slack_webhook_url_set", + "smtp_password_set", + "telegram_bot_token_set" + ] + }, + "ApiKeyRequest": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "makerspace": { + "type": "integer" + }, + "label": { + "type": "string", + "maxLength": 120 + }, + "reason": { + "type": "string" + }, + "allowed_origins": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusE94Enum" + } + ], + "readOnly": true + }, + "resolution_note": { + "type": "string", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "resolved_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "allowed_origins", + "created_at", + "id", + "label", + "makerspace", + "resolution_note", + "resolved_at", + "status" + ] + }, + "ApprovalModeE35Enum": { + "enum": [ + "instant", + "approve" + ], + "type": "string", + "description": "* `instant` - Instant confirmation\n* `approve` - Staff approval required" + }, + "ApproveRequest": { + "type": "object", + "properties": { + "role_id": { + "type": "integer" + }, + "plan_id": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "role_id" + ] + }, + "ArchiveCustodyReadiness": { + "type": "object", + "properties": { + "below_floor_makerspaces": { + "type": "integer", + "minimum": 0 + }, + "zero_recipient_makerspaces": { + "type": "integer", + "minimum": 0 + }, + "undelivered_alarms": { + "type": "integer", + "minimum": 0 + }, + "alarms_with_no_operator_address": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "alarms_with_no_operator_address", + "below_floor_makerspaces", + "undelivered_alarms", + "zero_recipient_makerspaces" + ] + }, + "ArchiveCustodyStateEnum": { + "type": "string", + "enum": [ + "healthy", + "not_applicable", + "degraded_one_recipient", + "floor_breached_zero" + ] + }, + "ArchiveRecipient": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "public_recipient": { + "type": "string", + "readOnly": true + }, + "fingerprint": { + "type": "string", + "readOnly": true + }, + "label": { + "type": "string", + "readOnly": true + }, + "added_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "added_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "compromised_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "verified_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "challenge_issued_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "added_at", + "added_by", + "challenge_issued_at", + "compromised_at", + "fingerprint", + "id", + "label", + "public_recipient", + "revoked_at", + "verified_at" + ] + }, + "ArchiveRecipientChallenge": { + "type": "object", + "properties": { + "recipient": { + "$ref": "#/components/schemas/ArchiveRecipient" + }, + "encrypted_challenge": { + "type": "string", + "description": "The binary age ciphertext encoded as unpadded base64url for JSON transport. Its decrypted plaintext is the 32-byte nonce encoded as canonical, unpadded base64url." + }, + "nonce_encoding": { + "type": "string", + "default": "base64url-unpadded", + "description": "The decrypted nonce uses canonical unpadded base64url." + } + }, + "required": [ + "encrypted_challenge", + "recipient" + ] + }, + "ArchiveRecipientCreate": { + "type": "object", + "properties": { + "public_recipient": { + "type": "string", + "maxLength": 200 + }, + "label": { + "type": "string", + "maxLength": 120 + } + }, + "required": [ + "label", + "public_recipient" + ] + }, + "ArchiveRecipientError": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + } + }, + "required": [ + "code", + "detail" + ] + }, + "ArchiveRecipientVerify": { + "type": "object", + "properties": { + "nonce": { + "type": "string", + "description": "The decrypted 32-byte nonce in canonical, unpadded base64url form. Padding and non-canonical encodings are refused.", + "maxLength": 128 + } + }, + "required": [ + "nonce" + ] + }, + "ArchiveRequestError": { + "type": "object", + "properties": { + "detail": { + "type": "string" + }, + "code": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "ArchiveRequestValidationError": { + "type": "object", + "description": "DRF field-keyed errors, which are NOT the `detail`/`code` shape.\n\nA blank or overlong reason fails in `serializer.is_valid(raise_exception=True)` and comes\nback as `{\"reason\": [\"...\"]}`. Declaring every 400 as `ArchiveRequestError` published a\ncontract the endpoint does not honour, so a generated client would destructure `detail`\nand find nothing. Follows the `ProvisionSubdomainValidationErrorSerializer` precedent.", + "properties": { + "reason": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "ArchivedPaymentMakerspace": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "slug": { + "type": "string", + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "ArchivedPaymentSummary": { + "type": "object", + "properties": { + "makerspace": { + "$ref": "#/components/schemas/ArchivedPaymentMakerspace" + }, + "pending_count": { + "type": "integer", + "minimum": 0 + }, + "total_count": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "makerspace", + "pending_count", + "total_count" + ] + }, + "AssetChainGroup": { + "type": "object", + "properties": { + "asset_id": { + "type": "integer", + "nullable": true + }, + "asset_tag": { + "type": "string" + }, + "serial_number": { + "type": "string" + }, + "status": { + "type": "string" + }, + "events": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TimelineEvent" + } + } + }, + "required": [ + "asset_id", + "asset_tag", + "events", + "serial_number", + "status" + ] + }, + "AssetGenerate": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "maximum": 200, + "minimum": 1 + }, + "name_prefix": { + "type": "string" + }, + "serial_numbers": { + "type": "array", + "items": { + "type": "string" + } + }, + "print_batch_id": { + "type": "integer", + "nullable": true + }, + "create_print_batch": { + "type": "boolean", + "default": false + } + }, + "required": [ + "count" + ] + }, + "AssetGenerateItem": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "asset_tag": { + "type": "string" + }, + "qr": { + "$ref": "#/components/schemas/QrCode" + } + }, + "required": [ + "asset_tag", + "id", + "qr" + ] + }, + "AssetGenerateResult": { + "type": "object", + "properties": { + "assets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AssetGenerateItem" + } + }, + "print_batch_id": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "assets", + "print_batch_id" + ] + }, + "AssetQrHistory": { + "type": "object", + "properties": { + "asset": { + "type": "integer" + }, + "scans": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QrHistoryItem" + } + } + }, + "required": [ + "asset", + "scans" + ] + }, + "AssignBox": { + "type": "object", + "properties": { + "box_code": { + "type": "string" + } + }, + "required": [ + "box_code" + ] + }, + "AssignOperator": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "access_level": { + "type": "string", + "maxLength": 16 + } + }, + "required": [ + "access_level", + "user_id" + ] + }, + "AssignableMachineType": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "is_global": { + "type": "boolean" + } + }, + "required": [ + "id", + "is_global", + "name", + "slug" + ] + }, + "AttendanceCorrectionResponse": { + "type": "object", + "properties": { + "registration_id": { + "type": "integer" + }, + "status": { + "type": "string" + }, + "revoked_certificates": { + "type": "integer" + } + }, + "required": [ + "registration_id", + "revoked_certificates", + "status" + ] + }, + "AttendedEvent": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "title": { + "type": "string" + }, + "starts_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "id", + "starts_at", + "title" + ] + }, + "Audience356Enum": { + "enum": [ + "member", + "staff" + ], + "type": "string", + "description": "* `member` - Member\n* `staff` - Staff" + }, + "Audience817Enum": { + "enum": [ + "member", + "staff" + ], + "type": "string", + "description": "* `member` - member\n* `staff` - staff" + }, + "AudienceBe5Enum": { + "enum": [ + "member", + "public" + ], + "type": "string", + "description": "* `member` - Active members\n* `public` - All accounts" + }, + "AuditLog": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "actor": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "action": { + "type": "string", + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "target_type": { + "type": "string", + "readOnly": true + }, + "target_id": { + "type": "string", + "readOnly": true + }, + "meta": { + "type": "object", + "additionalProperties": {}, + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "action_label": { + "type": "string", + "readOnly": true + }, + "actor_display": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "meta_redacted": { + "type": "boolean", + "readOnly": true + }, + "target_link": { + "allOf": [ + { + "$ref": "#/components/schemas/AuditTargetLink" + } + ], + "nullable": true, + "readOnly": true + } + }, + "required": [ + "action", + "action_label", + "actor", + "actor_display", + "created_at", + "id", + "makerspace", + "meta", + "meta_redacted", + "target_id", + "target_link", + "target_type" + ] + }, + "AuditTargetLink": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "label", + "path" + ] + }, + "AuditVerificationBadRequest": { + "oneOf": [ + { + "$ref": "#/components/schemas/AuditVerificationError" + }, + { + "$ref": "#/components/schemas/AuditVerificationFilterError" + } + ] + }, + "AuditVerificationDetailError": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "AuditVerificationError": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" + } + }, + "required": [ + "code", + "detail" + ] + }, + "AuditVerificationFilterError": { + "type": "object", + "properties": { + "action": { + "type": "array", + "items": { + "type": "string" + } + }, + "target_type": { + "type": "array", + "items": { + "type": "string" + } + }, + "target_id": { + "type": "array", + "items": { + "type": "string" + } + }, + "actor": { + "type": "array", + "items": { + "type": "string" + } + }, + "start": { + "type": "array", + "items": { + "type": "string" + } + }, + "end": { + "type": "array", + "items": { + "type": "string" + } + }, + "non_field_errors": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "AuthMembership": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "slug": { + "type": "string" + }, + "role": { + "type": "string", + "nullable": true + }, + "role_id": { + "type": "integer", + "nullable": true + }, + "role_ids": { + "type": "array", + "items": { + "type": "integer" + } + }, + "role_slugs": { + "type": "array", + "items": { + "type": "string" + } + }, + "role_names": { + "type": "array", + "items": { + "type": "string" + } + }, + "role_name": { + "type": "string" + }, + "role_slug": { + "type": "string", + "nullable": true + }, + "source": { + "$ref": "#/components/schemas/AuthMembershipSourceEnum" + }, + "actions": { + "type": "array", + "items": { + "type": "string" + } + }, + "can_configure_machine_types": { + "type": "boolean" + }, + "is_machine_only": { + "type": "boolean" + }, + "can_refer": { + "type": "boolean" + }, + "can_verify": { + "type": "boolean" + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "referrals_enabled": { + "type": "boolean" + } + }, + "required": [ + "actions", + "can_configure_machine_types", + "can_refer", + "can_verify", + "id", + "is_machine_only", + "referrals_enabled", + "role", + "role_id", + "role_ids", + "role_name", + "role_names", + "role_slug", + "role_slugs", + "slug", + "source", + "verified_at" + ] + }, + "AuthMembershipSourceEnum": { + "enum": [ + "membership", + "organization" + ], + "type": "string", + "description": "* `membership` - membership\n* `organization` - organization" + }, + "AuthUserPayload": { + "type": "object", + "properties": { + "session_authority": { + "type": "string", + "description": "Effective session surface: member, native_staff, staff, staff_api or verification_only. Native staff APIs require X-Makerspace-Id selecting an active local membership; URL and body targets must match." + }, + "id": { + "type": "integer" + }, + "username": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "display_name": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "email_verified": { + "type": "boolean" + }, + "role": { + "type": "string" + }, + "is_superuser": { + "type": "boolean" + }, + "must_change_password": { + "type": "boolean" + }, + "makerspaces": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AuthMembership" + } + }, + "event_grants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GrantedEventEntry" + } + }, + "booking_grants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GrantedBookingEntry" + } + } + }, + "required": [ + "display_name", + "email", + "email_verified", + "id", + "is_superuser", + "makerspaces", + "must_change_password", + "phone", + "role", + "session_authority", + "username" + ] + }, + "AvailabilityEnum": { + "type": "string", + "enum": [ + "Available", + "Limited", + "Full" + ] + }, + "BackupArchive": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "scope": { + "allOf": [ + { + "$ref": "#/components/schemas/ScopeEnum" + } + ], + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/BackupArchiveStatusEnum" + } + ], + "readOnly": true + }, + "manifest": { + "readOnly": true + }, + "size_bytes": { + "type": "integer", + "readOnly": true + }, + "age_encrypted": { + "type": "boolean", + "readOnly": true + }, + "failure_detail": { + "type": "string", + "readOnly": true + }, + "started_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "purge_warning": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "age_encrypted", + "completed_at", + "created_at", + "expires_at", + "failure_detail", + "id", + "makerspace", + "manifest", + "purge_warning", + "scope", + "size_bytes", + "started_at", + "status" + ] + }, + "BackupArchiveStatusEnum": { + "enum": [ + "pending", + "running", + "promoting", + "available", + "failed", + "expired" + ], + "type": "string", + "description": "* `pending` - Pending\n* `running` - Running\n* `promoting` - Promoting\n* `available` - Available\n* `failed` - Failed\n* `expired` - Expired" + }, + "BackupCustody": { + "type": "object", + "properties": { + "superadmin_access_enabled": { + "type": "boolean" + }, + "verified_recipient_count": { + "type": "integer" + }, + "archive_custody": { + "allOf": [ + { + "$ref": "#/components/schemas/CustodyState" + } + ], + "nullable": true + }, + "tenant_exit_custody": { + "allOf": [ + { + "$ref": "#/components/schemas/CustodyState" + } + ], + "nullable": true + } + }, + "required": [ + "archive_custody", + "superadmin_access_enabled", + "tenant_exit_custody", + "verified_recipient_count" + ] + }, + "BackupDestination": { + "type": "object", + "description": "Credentials are write-only; reads report only whether they are stored.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 64, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/BackupDestinationKindEnum" + } + ], + "readOnly": true + }, + "endpoint_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 300 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "bucket": { + "type": "string", + "maxLength": 255 + }, + "region": { + "type": "string", + "maxLength": 64 + }, + "prefix": { + "type": "string", + "maxLength": 200 + }, + "addressing_style": { + "$ref": "#/components/schemas/AddressingStyleEnum" + }, + "access_key_id": { + "type": "string", + "writeOnly": true, + "maxLength": 200 + }, + "secret_access_key": { + "type": "string", + "writeOnly": true, + "maxLength": 200 + }, + "credentials_set": { + "type": "boolean", + "readOnly": true + }, + "enabled": { + "type": "boolean" + }, + "retention_days": { + "type": "integer", + "maximum": 3650, + "minimum": 1 + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "retired_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_success_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_error_code": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "bucket", + "created_at", + "credentials_set", + "id", + "kind", + "last_error_code", + "last_success_at", + "name", + "retired_at", + "updated_at" + ] + }, + "BackupDestinationKindEnum": { + "enum": [ + "s3" + ], + "type": "string", + "description": "* `s3` - S3-compatible (AWS, R2, Supabase Storage, MinIO)" + }, + "BackupDestinationPage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BackupDestination" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "BackupDestinationUpload": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "destination": { + "type": "string", + "readOnly": true + }, + "archive_id_snapshot": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "scope_snapshot": { + "type": "string", + "readOnly": true + }, + "makerspace_id_snapshot": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "archive_completed_at_snapshot": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "expected_size_bytes": { + "type": "integer", + "readOnly": true + }, + "expected_sha256": { + "type": "string", + "readOnly": true + }, + "integrity_source": { + "allOf": [ + { + "$ref": "#/components/schemas/IntegritySourceEnum" + } + ], + "readOnly": true + }, + "remote_locator": { + "type": "string", + "readOnly": true + }, + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/BackupDestinationUploadStateEnum" + } + ], + "readOnly": true + }, + "attempts": { + "type": "integer", + "readOnly": true + }, + "next_attempt_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "uploaded_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "verified_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "deleted_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_error_code": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "archive_completed_at_snapshot", + "archive_id_snapshot", + "attempts", + "deleted_at", + "destination", + "expected_sha256", + "expected_size_bytes", + "expires_at", + "id", + "integrity_source", + "last_error_code", + "makerspace_id_snapshot", + "next_attempt_at", + "remote_locator", + "scope_snapshot", + "state", + "uploaded_at", + "verified_at" + ] + }, + "BackupDestinationUploadPage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BackupDestinationUpload" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "BackupDestinationUploadStateEnum": { + "enum": [ + "pending", + "uploading", + "bytes_verified", + "abandoned", + "deleted" + ], + "type": "string", + "description": "* `pending` - Pending upload\n* `uploading` - Uploading\n* `bytes_verified` - Bytes verified\n* `abandoned` - Abandoned (source no longer available)\n* `deleted` - Deleted by retention" + }, + "BackupDownload": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "purge_warning": { + "type": "string" + } + }, + "required": [ + "expires_at", + "purge_warning", + "url" + ] + }, + "BadgePdfRequest": { + "type": "object", + "properties": { + "registration_ids": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1 + }, + "maxItems": 200 + }, + "template_override": { + "allOf": [ + { + "$ref": "#/components/schemas/BadgeTemplate" + } + ], + "nullable": true + }, + "include_attended": { + "type": "boolean", + "default": false + } + }, + "required": [ + "registration_ids" + ] + }, + "BadgeTemplate": { + "type": "object", + "properties": { + "version": { + "type": "integer" + }, + "paper_size": { + "$ref": "#/components/schemas/PaperSizeEnum" + }, + "orientation": { + "$ref": "#/components/schemas/OrientationEnum" + }, + "page_width_mm": { + "type": "number", + "format": "double", + "nullable": true + }, + "page_height_mm": { + "type": "number", + "format": "double", + "nullable": true + }, + "card_width_mm": { + "type": "number", + "format": "double" + }, + "card_height_mm": { + "type": "number", + "format": "double" + }, + "margin_mm": { + "type": "number", + "format": "double" + }, + "gap_mm": { + "type": "number", + "format": "double" + }, + "template": { + "type": "string" + }, + "fields": { + "type": "array", + "items": { + "type": "string", + "maxLength": 80 + } + }, + "font_size_pt": { + "type": "number", + "format": "double" + }, + "name_font_size_pt": { + "type": "number", + "format": "double" + }, + "text_align": { + "$ref": "#/components/schemas/TextAlignEnum" + }, + "include_qr": { + "type": "boolean" + }, + "grid_mm": { + "type": "number", + "format": "double", + "maximum": 10, + "minimum": 0.5 + }, + "elements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PositionedElement" + } + } + } + }, + "BillingPolicyError": { + "oneOf": [ + { + "$ref": "#/components/schemas/HardwareRequestError" + }, + { + "$ref": "#/components/schemas/BillingPolicyValidationError" + } + ] + }, + "BillingPolicyValidationError": { + "type": "object", + "properties": { + "number_prefix": { + "type": "array", + "items": { + "type": "string" + } + }, + "legal_name": { + "type": "array", + "items": { + "type": "string" + } + }, + "address": { + "type": "array", + "items": { + "type": "string" + } + }, + "tax_id": { + "type": "array", + "items": { + "type": "string" + } + }, + "footer": { + "type": "array", + "items": { + "type": "string" + } + }, + "account_codes": {}, + "payment_terms_days": { + "type": "array", + "items": { + "type": "string" + } + }, + "reminder_days_before": {}, + "reminder_days_after": {}, + "max_reminders": { + "type": "array", + "items": { + "type": "string" + } + }, + "non_field_errors": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "BillingSettings": { + "type": "object", + "properties": { + "payment_terms_days": { + "type": "integer", + "maximum": 365, + "minimum": 0 + }, + "reminder_days_before": { + "type": "array", + "items": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "maxItems": 20 + }, + "reminder_days_after": { + "type": "array", + "items": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "maxItems": 20 + }, + "max_reminders": { + "type": "integer", + "maximum": 20, + "minimum": 0 + }, + "number_prefix": { + "type": "string", + "maxLength": 32 + }, + "legal_name": { + "type": "string", + "maxLength": 150 + }, + "address": { + "type": "string", + "maxLength": 1000 + }, + "tax_id": { + "type": "string", + "maxLength": 64 + }, + "footer": { + "type": "string", + "maxLength": 1000 + }, + "account_codes": {} + }, + "required": [ + "max_reminders", + "reminder_days_after", + "reminder_days_before" + ] + }, + "Binding": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true + }, + "device": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "machine": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "enabled": { + "type": "boolean", + "readOnly": true + }, + "auto_apply_terminal": { + "type": "boolean", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "auto_apply_terminal", + "created_at", + "device", + "enabled", + "id", + "machine", + "makerspace", + "updated_at" + ] + }, + "BindingCreate": { + "type": "object", + "properties": { + "device_id": { + "type": "string", + "format": "uuid" + }, + "machine_id": { + "type": "integer", + "minimum": 1 + }, + "enabled": { + "type": "boolean", + "default": true + }, + "auto_apply_terminal": { + "type": "boolean", + "default": false + } + }, + "required": [ + "device_id", + "machine_id" + ] + }, + "BlankEnum": { + "enum": [ + "" + ] + }, + "BookableSpaceAdmin": { + "type": "object", + "properties": { + "clone_enabled": { + "type": "boolean", + "readOnly": true + }, + "staff_tools_enabled": { + "type": "boolean", + "readOnly": true + }, + "can_copy_booking_rules": { + "type": "boolean", + "readOnly": true + }, + "recurring_enabled": { + "type": "boolean", + "readOnly": true + }, + "staff_grants_enabled": { + "type": "boolean", + "readOnly": true + }, + "broadcasts_enabled": { + "type": "boolean", + "readOnly": true + }, + "staff_permissions": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "can_manage_grants": { + "type": "boolean", + "readOnly": true + }, + "report_features": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "checkin_enabled": { + "type": "boolean", + "readOnly": true + }, + "passes_enabled": { + "type": "boolean", + "readOnly": true + }, + "website_enabled": { + "type": "boolean", + "readOnly": true + }, + "embed_enabled": { + "type": "boolean", + "readOnly": true + }, + "id": { + "type": "integer", + "readOnly": true + }, + "public_token": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "makerspace_id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + }, + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/Kind3bfEnum" + } + ], + "readOnly": true + }, + "description": { + "type": "string", + "readOnly": true + }, + "capacity": { + "type": "integer", + "readOnly": true + }, + "location": { + "type": "string", + "readOnly": true + }, + "image_url": { + "type": "string", + "readOnly": true + }, + "is_public": { + "type": "boolean", + "readOnly": true + }, + "show_public_availability": { + "type": "boolean", + "readOnly": true + }, + "show_public_booker_names": { + "type": "boolean", + "readOnly": true + }, + "approval_mode": { + "allOf": [ + { + "$ref": "#/components/schemas/ApprovalModeE35Enum" + } + ], + "readOnly": true + }, + "min_booking_duration_minutes": { + "type": "integer", + "readOnly": true + }, + "max_booking_duration_minutes": { + "type": "integer", + "readOnly": true + }, + "booking_lead_time_minutes": { + "type": "integer", + "readOnly": true + }, + "max_booking_advance_days": { + "type": "integer", + "readOnly": true + }, + "buffer_before_minutes": { + "type": "integer", + "readOnly": true + }, + "buffer_after_minutes": { + "type": "integer", + "readOnly": true + }, + "cancellation_cutoff_hours": { + "type": "integer", + "readOnly": true + }, + "custom_form": { + "readOnly": true, + "nullable": true + }, + "requester_notifications_enabled": { + "type": "boolean", + "readOnly": true, + "nullable": true + }, + "payment_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true + }, + "machine_type": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "effective_requester_notifications_enabled": { + "type": "boolean", + "readOnly": true + }, + "is_active": { + "type": "boolean", + "readOnly": true + }, + "created_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "approval_mode", + "booking_lead_time_minutes", + "broadcasts_enabled", + "buffer_after_minutes", + "buffer_before_minutes", + "can_copy_booking_rules", + "can_manage_grants", + "cancellation_cutoff_hours", + "capacity", + "checkin_enabled", + "clone_enabled", + "created_at", + "created_by_id", + "custom_form", + "description", + "effective_requester_notifications_enabled", + "embed_enabled", + "id", + "image_url", + "is_active", + "is_public", + "kind", + "location", + "machine_type", + "makerspace_id", + "max_booking_advance_days", + "max_booking_duration_minutes", + "min_booking_duration_minutes", + "name", + "passes_enabled", + "payment_amount", + "public_token", + "recurring_enabled", + "report_features", + "requester_notifications_enabled", + "show_public_availability", + "show_public_booker_names", + "staff_grants_enabled", + "staff_permissions", + "staff_tools_enabled", + "updated_at", + "website_enabled" + ] + }, + "BookableSpaceBookingRules": { + "type": "object", + "properties": { + "min_booking_duration_minutes": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1 + }, + "max_booking_duration_minutes": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1 + }, + "booking_lead_time_minutes": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "max_booking_advance_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1 + }, + "buffer_before_minutes": { + "type": "integer", + "maximum": 1440, + "minimum": 0 + }, + "buffer_after_minutes": { + "type": "integer", + "maximum": 1440, + "minimum": 0 + }, + "cancellation_cutoff_hours": { + "type": "integer", + "maximum": 8760, + "minimum": 0 + }, + "approval_mode": { + "$ref": "#/components/schemas/ApprovalModeE35Enum" + } + } + }, + "BookableSpaceListResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookableSpaceAdmin" + } + } + }, + "required": [ + "count", + "results" + ] + }, + "BookableSpaceWrite": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 200 + }, + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/Kind3bfEnum" + } + ], + "default": "other" + }, + "description": { + "type": "string", + "default": "" + }, + "capacity": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "location": { + "type": "string", + "default": "", + "maxLength": 255 + }, + "is_public": { + "type": "boolean", + "default": false + }, + "show_public_availability": { + "type": "boolean", + "default": false + }, + "show_public_booker_names": { + "type": "boolean", + "default": false + }, + "custom_form": { + "nullable": true + }, + "requester_notifications_enabled": { + "type": "boolean", + "nullable": true + }, + "payment_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "default": "0.00" + }, + "machine_type": { + "type": "integer", + "minimum": 1, + "nullable": true + } + }, + "required": [ + "name" + ] + }, + "BookingAdmin": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "public_token": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "space_id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + }, + "email": { + "type": "string", + "readOnly": true + }, + "phone": { + "type": "string", + "readOnly": true + }, + "starts_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "ends_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/Status2caEnum" + } + ], + "readOnly": true + }, + "arrived_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "note": { + "type": "string", + "readOnly": true + }, + "custom_answers": { + "readOnly": true, + "nullable": true + }, + "cancelled_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "status_before_cancel": { + "type": "string", + "readOnly": true + }, + "restore_until": { + "type": "string", + "format": "date-time", + "nullable": true, + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "payment": { + "allOf": [ + { + "$ref": "#/components/schemas/StaffPaymentSummary" + } + ], + "nullable": true, + "readOnly": true + } + }, + "required": [ + "arrived_at", + "cancelled_at", + "created_at", + "custom_answers", + "email", + "ends_at", + "id", + "name", + "note", + "payment", + "phone", + "public_token", + "restore_until", + "space_id", + "starts_at", + "status", + "status_before_cancel" + ] + }, + "BookingAnswerReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "report_key": { + "type": "string" + }, + "meta": { + "$ref": "#/components/schemas/BookingReportMeta" + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingAnswerReportRow" + } + } + }, + "required": [ + "meta", + "report_key", + "rows", + "typed_rows" + ] + }, + "BookingAnswerReportRow": { + "type": "object", + "properties": { + "booking_id": { + "type": "integer" + }, + "space_id": { + "type": "integer" + }, + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string" + }, + "answers": { + "type": "string", + "description": "JSON of the stored custom-answer snapshot; null when absent." + } + }, + "required": [ + "answers", + "booking_id", + "ends_at", + "space_id", + "starts_at", + "status" + ] + }, + "BookingArrival": { + "type": "object", + "properties": { + "booking_id": { + "type": "integer" + }, + "display_name": { + "type": "string" + }, + "space_name": { + "type": "string" + }, + "timezone": { + "type": "string" + }, + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + }, + "status": { + "$ref": "#/components/schemas/Status2caEnum" + }, + "arrived_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "can_arrive": { + "type": "boolean" + }, + "can_reverse": { + "type": "boolean" + }, + "can_print_pass": { + "type": "boolean", + "readOnly": true + }, + "waiver_state": { + "$ref": "#/components/schemas/WaiverStateEnum" + } + }, + "required": [ + "arrived_at", + "booking_id", + "can_arrive", + "can_print_pass", + "can_reverse", + "display_name", + "ends_at", + "space_name", + "starts_at", + "status", + "timezone", + "waiver_state" + ] + }, + "BookingBroadcast": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "space": { + "type": "integer", + "readOnly": true + }, + "subject": { + "type": "string", + "readOnly": true, + "maxLength": 200 + }, + "body": { + "type": "string", + "readOnly": true, + "maxLength": 10000 + }, + "window_start": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "window_end": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "statuses": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Statuses219Enum" + }, + "readOnly": true, + "maxItems": 2, + "minItems": 1 + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusAdeEnum" + } + ], + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "queued_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "body", + "completed_at", + "created_at", + "id", + "queued_at", + "space", + "status", + "statuses", + "subject", + "window_end", + "window_start" + ] + }, + "BookingBroadcastInput": { + "type": "object", + "properties": { + "subject": { + "type": "string", + "maxLength": 200 + }, + "body": { + "type": "string", + "maxLength": 10000 + }, + "window_start": { + "type": "string", + "format": "date-time" + }, + "window_end": { + "type": "string", + "format": "date-time" + }, + "statuses": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Statuses219Enum" + }, + "default": [ + "confirmed" + ], + "maxItems": 2, + "minItems": 1 + } + }, + "required": [ + "body", + "subject", + "window_end" + ] + }, + "BookingBroadcastList": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingBroadcast" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "BookingCloneInput": { + "type": "object", + "properties": { + "copy_booking_rules": { + "type": "boolean", + "default": false + } + } + }, + "BookingConfirmation": { + "type": "object", + "properties": { + "public_token": { + "type": "string", + "format": "uuid" + }, + "space_name": { + "type": "string" + }, + "timezone": { + "type": "string" + }, + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + }, + "status": { + "$ref": "#/components/schemas/Status2caEnum" + }, + "location": { + "type": "string" + }, + "description": { + "type": "string" + }, + "arrived_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "can_cancel": { + "type": "boolean" + }, + "cancellation_cutoff_at": { + "type": "string", + "format": "date-time" + }, + "pass_available": { + "type": "boolean", + "readOnly": true + }, + "qr_value": { + "type": "string", + "nullable": true + }, + "google_calendar_url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "arrived_at", + "can_cancel", + "cancellation_cutoff_at", + "description", + "ends_at", + "google_calendar_url", + "location", + "pass_available", + "public_token", + "qr_value", + "space_name", + "starts_at", + "status", + "timezone" + ] + }, + "BookingCredential": { + "type": "object", + "properties": { + "credential": { + "type": "string", + "writeOnly": true, + "maxLength": 128 + } + }, + "required": [ + "credential" + ] + }, + "BookingEligibleMember": { + "type": "object", + "properties": { + "member_id": { + "type": "integer", + "readOnly": true + }, + "display_name": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "display_name", + "member_id" + ] + }, + "BookingEligibleMemberList": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingEligibleMember" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "BookingLeadTimeReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingLeadTimeRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "BookingLeadTimeRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "period": { + "type": "string", + "format": "date-time" + }, + "lead_time_bucket": { + "type": "string" + }, + "booking_count": { + "type": "integer" + } + }, + "required": [ + "booking_count", + "lead_time_bucket", + "period" + ] + }, + "BookingListResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingAdmin" + } + } + }, + "required": [ + "count", + "results" + ] + }, + "BookingMachineType": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "id", + "name" + ] + }, + "BookingMachineTypePage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingMachineType" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "BookingOccupancyReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingOccupancyRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "BookingOccupancyRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "period": { + "type": "string", + "format": "date-time" + }, + "weekday": { + "type": "integer", + "maximum": 6, + "minimum": 0 + }, + "hour": { + "type": "integer", + "maximum": 23, + "minimum": 0 + }, + "occupied_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + }, + "window_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + }, + "occupancy_percent": { + "type": "number", + "format": "double", + "nullable": true + } + }, + "required": [ + "hour", + "occupancy_percent", + "occupied_hours", + "period", + "weekday", + "window_hours" + ] + }, + "BookingPassElement": { + "type": "object", + "properties": { + "field": { + "$ref": "#/components/schemas/FieldEnum" + }, + "x_mm": { + "type": "number", + "format": "double", + "minimum": 0 + }, + "y_mm": { + "type": "number", + "format": "double", + "minimum": 0 + }, + "width_mm": { + "type": "number", + "format": "double", + "minimum": 0.5 + }, + "height_mm": { + "type": "number", + "format": "double", + "minimum": 0.5 + }, + "font": { + "$ref": "#/components/schemas/FontEnum" + }, + "font_size_pt": { + "type": "number", + "format": "double", + "maximum": 48, + "minimum": 6 + }, + "bold": { + "type": "boolean" + }, + "align": { + "$ref": "#/components/schemas/AlignEnum" + }, + "colour": { + "type": "string", + "pattern": "^#[0-9A-Fa-f]{6}$" + } + }, + "required": [ + "field", + "height_mm", + "width_mm", + "x_mm", + "y_mm" + ] + }, + "BookingPassTemplate": { + "type": "object", + "properties": { + "version": { + "type": "integer", + "maximum": 2, + "minimum": 2 + }, + "paper_size": { + "$ref": "#/components/schemas/PaperSizeEnum" + }, + "orientation": { + "$ref": "#/components/schemas/OrientationEnum" + }, + "page_width_mm": { + "type": "number", + "format": "double", + "maximum": 500, + "minimum": 100, + "nullable": true + }, + "page_height_mm": { + "type": "number", + "format": "double", + "maximum": 500, + "minimum": 100, + "nullable": true + }, + "card_width_mm": { + "type": "number", + "format": "double", + "maximum": 150, + "minimum": 40 + }, + "card_height_mm": { + "type": "number", + "format": "double", + "maximum": 120, + "minimum": 30 + }, + "margin_mm": { + "type": "number", + "format": "double", + "maximum": 40, + "minimum": 0 + }, + "gap_mm": { + "type": "number", + "format": "double", + "maximum": 30, + "minimum": 0 + }, + "grid_mm": { + "type": "number", + "format": "double", + "maximum": 10, + "minimum": 0.5 + }, + "elements": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingPassElement" + } + } + } + }, + "BookingPublicRules": { + "type": "object", + "properties": { + "minimum_minutes": { + "type": "integer" + }, + "maximum_minutes": { + "type": "integer" + }, + "lead_minutes": { + "type": "integer" + }, + "advance_days": { + "type": "integer" + }, + "cancellation_cutoff_hours": { + "type": "integer" + } + }, + "required": [ + "advance_days", + "cancellation_cutoff_hours", + "lead_minutes", + "maximum_minutes", + "minimum_minutes" + ] + }, + "BookingReportMeta": { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "grain": { + "type": "string" + }, + "rollup_through": { + "type": "string", + "nullable": true + }, + "arrival_counts_available": { + "type": "boolean" + } + }, + "required": [ + "grain", + "rollup_through", + "source" + ] + }, + "BookingReschedule": { + "type": "object", + "properties": { + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "ends_at", + "starts_at" + ] + }, + "BookingReservationReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "report_key": { + "type": "string" + }, + "meta": { + "$ref": "#/components/schemas/BookingReportMeta" + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingReservationReportRow" + } + } + }, + "required": [ + "meta", + "report_key", + "rows", + "typed_rows" + ] + }, + "BookingReservationReportRow": { + "type": "object", + "properties": { + "booking_id": { + "type": "integer" + }, + "space_id": { + "type": "integer" + }, + "space_name": { + "type": "string" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "booking_id", + "created_at", + "email", + "ends_at", + "name", + "phone", + "space_id", + "space_name", + "starts_at", + "status" + ] + }, + "BookingResourceUtilizationReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "report_key": { + "type": "string" + }, + "meta": { + "$ref": "#/components/schemas/BookingReportMeta" + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingResourceUtilizationRow" + } + } + }, + "required": [ + "meta", + "report_key", + "rows", + "typed_rows" + ] + }, + "BookingResourceUtilizationRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "space_id": { + "type": "integer" + }, + "space_name": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "booked": { + "type": "integer" + }, + "completed": { + "type": "integer" + }, + "no_show": { + "type": "integer" + }, + "cancelled": { + "type": "integer" + }, + "upcoming": { + "type": "integer" + }, + "reserved_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "completed_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "window_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "reservation_utilization_percent": { + "type": "number", + "format": "double", + "nullable": true + }, + "no_show_rate_percent": { + "type": "number", + "format": "double", + "nullable": true + }, + "arrived_reservations": { + "type": "integer", + "description": "Reservations with a current, unreversed arrival." + }, + "confirmed_without_recorded_arrival": { + "type": "integer", + "description": "Confirmed reservations with no current arrival; not a no-show count." + } + }, + "required": [ + "arrived_reservations", + "booked", + "cancelled", + "completed", + "completed_hours", + "confirmed_without_recorded_arrival", + "is_active", + "kind", + "no_show", + "no_show_rate_percent", + "reservation_utilization_percent", + "reserved_hours", + "space_id", + "space_name", + "upcoming", + "window_hours" + ] + }, + "BookingStaffGrant": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true + }, + "space": { + "type": "integer", + "readOnly": true + }, + "user": { + "type": "integer", + "readOnly": true + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Permissions43eEnum" + }, + "readOnly": true, + "maxItems": 4, + "minItems": 1 + }, + "granted_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_by": { + "type": "integer", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "created_at", + "granted_by", + "id", + "makerspace", + "permissions", + "revoked_at", + "revoked_by", + "space", + "user" + ] + }, + "BookingStaffGrantInvite": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true + }, + "space": { + "type": "integer", + "readOnly": true + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Permissions43eEnum" + }, + "readOnly": true, + "maxItems": 4, + "minItems": 1 + }, + "created_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "redeemed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "created_at", + "created_by", + "expires_at", + "id", + "makerspace", + "permissions", + "redeemed_at", + "revoked_at", + "space" + ] + }, + "BookingStaffGrantInviteCreated": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true + }, + "space": { + "type": "integer", + "readOnly": true + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Permissions43eEnum" + }, + "readOnly": true, + "maxItems": 4, + "minItems": 1 + }, + "created_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "redeemed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "token": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "created_at", + "created_by", + "expires_at", + "id", + "makerspace", + "permissions", + "redeemed_at", + "revoked_at", + "space", + "token" + ] + }, + "BookingStaffGrantInviteInput": { + "type": "object", + "properties": { + "permissions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Permissions43eEnum" + }, + "maxItems": 4, + "minItems": 1 + }, + "email": { + "oneOf": [ + { + "type": "string", + "format": "email", + "default": "" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "send_email": { + "type": "boolean", + "default": false + } + }, + "required": [ + "permissions" + ] + }, + "BookingStaffGrantInviteList": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingStaffGrantInvite" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "BookingStaffGrantList": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingStaffGrant" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "BookingStaffGrantRedeem": { + "type": "object", + "properties": { + "token": { + "type": "string", + "writeOnly": true, + "maxLength": 128, + "minLength": 32 + } + }, + "required": [ + "token" + ] + }, + "BookingTimeInput": { + "type": "object", + "properties": { + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "ends_at", + "starts_at" + ] + }, + "BookingTopMemberRow": { + "type": "object", + "properties": { + "member_id": { + "type": "integer", + "nullable": true + }, + "member": { + "type": "string" + }, + "booking_count": { + "type": "integer" + }, + "reserved_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + } + }, + "required": [ + "booking_count", + "member", + "member_id", + "reserved_hours" + ] + }, + "BookingTopMembersReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingTopMemberRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "BookingUtilizationReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingUtilizationRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "BookingUtilizationRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "space_id": { + "type": "integer" + }, + "space_name": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "booked": { + "type": "integer" + }, + "completed": { + "type": "integer" + }, + "no_show": { + "type": "integer" + }, + "cancelled": { + "type": "integer" + }, + "upcoming": { + "type": "integer" + }, + "reserved_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "completed_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "window_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "reservation_utilization_percent": { + "type": "number", + "format": "double", + "nullable": true + }, + "no_show_rate_percent": { + "type": "number", + "format": "double", + "nullable": true + } + }, + "required": [ + "booked", + "cancelled", + "completed", + "completed_hours", + "is_active", + "kind", + "no_show", + "no_show_rate_percent", + "reservation_utilization_percent", + "reserved_hours", + "space_id", + "space_name", + "upcoming", + "window_hours" + ] + }, + "BookingUtilizationV2Report": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingUtilizationV2Row" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "BookingUtilizationV2Row": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "space_id": { + "type": "integer" + }, + "space_name": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "booked": { + "type": "integer" + }, + "completed": { + "type": "integer" + }, + "no_show": { + "type": "integer" + }, + "cancelled": { + "type": "integer" + }, + "upcoming": { + "type": "integer" + }, + "reserved_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "completed_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "window_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "reservation_utilization_percent": { + "type": "number", + "format": "double", + "nullable": true + }, + "no_show_rate_percent": { + "type": "number", + "format": "double", + "nullable": true + }, + "period": { + "type": "string", + "format": "date-time" + }, + "reservations": { + "type": "integer" + }, + "terminal_reservations": { + "type": "integer" + }, + "cancellation_rate_percent": { + "type": "number", + "format": "double", + "nullable": true + } + }, + "required": [ + "booked", + "cancelled", + "completed", + "completed_hours", + "is_active", + "kind", + "no_show", + "no_show_rate_percent", + "reservation_utilization_percent", + "reserved_hours", + "space_id", + "space_name", + "upcoming", + "window_hours" + ] + }, + "BookingWebsite": { + "type": "object", + "properties": { + "public_url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "embed_path": { + "type": "string", + "readOnly": true + }, + "revision": { + "type": "integer" + }, + "released": { + "type": "boolean" + }, + "sections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingWebsiteSection" + } + }, + "assets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingWebsiteAsset" + } + } + }, + "required": [ + "embed_path", + "public_url", + "released", + "revision", + "sections" + ] + }, + "BookingWebsiteAsset": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "alt_text": { + "type": "string" + }, + "size_bytes": { + "type": "integer" + } + }, + "required": [ + "alt_text", + "id", + "size_bytes" + ] + }, + "BookingWebsiteAssetInput": { + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "uri" + }, + "alt_text": { + "type": "string", + "maxLength": 200 + }, + "replace_id": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "alt_text", + "file" + ] + }, + "BookingWebsiteContentInput": { + "type": "object", + "properties": { + "revision": { + "type": "integer", + "minimum": 0 + }, + "sections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BookingWebsiteSectionInput" + } + } + }, + "required": [ + "revision", + "sections" + ] + }, + "BookingWebsiteReleaseInput": { + "type": "object", + "properties": { + "revision": { + "type": "integer", + "minimum": 1 + }, + "released": { + "type": "boolean" + } + }, + "required": [ + "released", + "revision" + ] + }, + "BookingWebsiteSection": { + "type": "object", + "properties": { + "kind": { + "$ref": "#/components/schemas/KindAf4Enum" + }, + "slug": { + "type": "string", + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "title": { + "type": "string", + "maxLength": 200 + }, + "body": { + "type": "string", + "default": "", + "maxLength": 10000 + }, + "url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "default": "", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "asset_id": { + "type": "integer", + "minimum": 1, + "nullable": true + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/VisibilityE49Enum" + } + ], + "default": "draft" + }, + "position": { + "type": "integer", + "maximum": 1000, + "minimum": 0, + "default": 0 + }, + "image_url": { + "type": "string", + "nullable": true + }, + "image_alt_text": { + "type": "string" + } + }, + "required": [ + "image_alt_text", + "image_url", + "kind", + "slug", + "title" + ] + }, + "BookingWebsiteSectionInput": { + "type": "object", + "properties": { + "kind": { + "$ref": "#/components/schemas/KindAf4Enum" + }, + "slug": { + "type": "string", + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "title": { + "type": "string", + "maxLength": 200 + }, + "body": { + "type": "string", + "default": "", + "maxLength": 10000 + }, + "url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "default": "", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "asset_id": { + "type": "integer", + "minimum": 1, + "nullable": true + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/VisibilityE49Enum" + } + ], + "default": "draft" + }, + "position": { + "type": "integer", + "maximum": 1000, + "minimum": 0, + "default": 0 + } + }, + "required": [ + "kind", + "slug", + "title" + ] + }, + "Bounce": { + "type": "object", + "properties": { + "event_id": { + "type": "string", + "maxLength": 200 + }, + "message_id": { + "type": "string", + "maxLength": 255 + }, + "address": { + "type": "string", + "format": "email", + "maxLength": 254 + }, + "reason": { + "$ref": "#/components/schemas/BounceReasonEnum" + } + }, + "required": [ + "address", + "event_id", + "reason" + ] + }, + "BounceReasonEnum": { + "enum": [ + "bounce", + "complaint" + ], + "type": "string", + "description": "* `bounce` - bounce\n* `complaint` - complaint" + }, + "BounceResult": { + "type": "object", + "properties": { + "accepted": { + "type": "boolean" + } + }, + "required": [ + "accepted" + ] + }, + "Box": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true + }, + "parent": { + "type": "integer", + "nullable": true + }, + "code": { + "type": "string", + "readOnly": true + }, + "label": { + "type": "string", + "maxLength": 200 + }, + "location": { + "type": "string", + "maxLength": 200 + }, + "description": { + "type": "string" + }, + "kit_components": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitComponent" + }, + "maxItems": 50 + }, + "is_active": { + "type": "boolean" + }, + "qr_code_id": { + "type": "integer", + "nullable": true, + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "code", + "created_at", + "id", + "label", + "makerspace", + "qr_code_id", + "updated_at" + ] + }, + "Broadcast": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "title": { + "type": "string", + "readOnly": true + }, + "body": { + "type": "string", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "body", + "created_at", + "id", + "title" + ] + }, + "BroadcastInput": { + "type": "object", + "properties": { + "title": { + "type": "string", + "maxLength": 200 + }, + "body": { + "type": "string", + "maxLength": 12000 + }, + "plan_id": { + "type": "integer", + "minimum": 1 + }, + "status": { + "$ref": "#/components/schemas/BroadcastInputStatusEnum" + }, + "certification_id": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "body", + "title" + ] + }, + "BroadcastInputStatusEnum": { + "enum": [ + "active", + "lapsed" + ], + "type": "string", + "description": "* `active` - active\n* `lapsed` - lapsed" + }, + "BroadcastOptions": { + "type": "object", + "properties": { + "plans": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SegmentChoice" + } + }, + "certifications": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SegmentChoice" + } + } + }, + "required": [ + "certifications", + "plans" + ] + }, + "BroadcastResult": { + "type": "object", + "properties": { + "broadcast": { + "$ref": "#/components/schemas/Broadcast" + }, + "recipients": { + "type": "integer" + } + }, + "required": [ + "broadcast", + "recipients" + ] + }, + "BulkImportJob": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "mode": { + "allOf": [ + { + "$ref": "#/components/schemas/Mode087Enum" + } + ], + "readOnly": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/BulkImportJobStatusEnum" + } + ], + "readOnly": true + }, + "total_rows": { + "type": "integer", + "readOnly": true + }, + "processed_rows": { + "type": "integer", + "readOnly": true + }, + "created_count": { + "type": "integer", + "readOnly": true + }, + "updated_count": { + "type": "integer", + "readOnly": true + }, + "error_count": { + "type": "integer", + "readOnly": true + }, + "warning_count": { + "type": "integer", + "readOnly": true + }, + "result": { + "readOnly": true + }, + "error": { + "type": "string", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "completed_at", + "created_at", + "created_count", + "error", + "error_count", + "id", + "mode", + "processed_rows", + "result", + "status", + "total_rows", + "updated_at", + "updated_count", + "warning_count" + ] + }, + "BulkImportJobCreate": { + "type": "object", + "properties": { + "mode": { + "$ref": "#/components/schemas/Mode087Enum" + }, + "file": { + "type": "string", + "format": "uri", + "nullable": true + }, + "rows": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + }, + "maxItems": 5000 + }, + "mapping": {} + }, + "required": [ + "mode" + ] + }, + "BulkImportJobStatusEnum": { + "enum": [ + "pending", + "running", + "completed", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `running` - Running\n* `completed` - Completed\n* `failed` - Failed" + }, + "BulkImportPreview": { + "type": "object", + "properties": { + "file": { + "type": "string", + "format": "uri", + "nullable": true + }, + "rows": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + }, + "maxItems": 5000 + }, + "mapping": {} + } + }, + "BulkInviteInput": { + "type": "object", + "properties": { + "emails": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "maxItems": 200, + "minItems": 1 + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "send_email": { + "type": "boolean", + "default": true + } + }, + "required": [ + "emails" + ] + }, + "BulkInviteResult": { + "type": "object", + "properties": { + "created": { + "type": "integer" + } + }, + "required": [ + "created" + ] + }, + "CadenceEnum": { + "enum": [ + "daily", + "weekly", + "monthly" + ], + "type": "string", + "description": "* `daily` - Daily\n* `weekly` - Weekly\n* `monthly` - Monthly" + }, + "CashBookReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CashPeriod" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "CashPeriod": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "collected_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "refunded_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "period": { + "type": "string", + "format": "date" + }, + "method": { + "type": "string" + }, + "net_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + } + }, + "required": [ + "collected_amount", + "currency", + "method", + "net_amount", + "period", + "refunded_amount" + ] + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + }, + "slug": { + "type": "string", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "CategoryAdmin": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "slug": { + "oneOf": [ + { + "type": "string", + "pattern": "^[-a-zA-Z0-9_]+$" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "display_order": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "loan_days": { + "type": "integer", + "maximum": 365, + "minimum": 1, + "nullable": true + }, + "max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "nullable": true + }, + "icon": { + "type": "string", + "maxLength": 50 + }, + "product_count": { + "type": "integer", + "readOnly": true, + "default": 0 + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "created_at", + "id", + "makerspace", + "name", + "product_count", + "updated_at" + ] + }, + "CategoryInput": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 + }, + "slug": { + "type": "string", + "maxLength": 100, + "pattern": "^[-a-zA-Z0-9_]+$" + } + }, + "required": [ + "name", + "slug" + ] + }, + "CategoryList": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Category" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "CertificateArtwork": { + "type": "object", + "properties": { + "id": { + "type": "integer" + } + }, + "required": [ + "id" + ] + }, + "CertificateArtworkPresign": { + "type": "object", + "properties": { + "content_type": { + "$ref": "#/components/schemas/ContentTypeEnum" + }, + "content_length": { + "type": "integer", + "maximum": 10485760, + "minimum": 1 + } + }, + "required": [ + "content_length", + "content_type" + ] + }, + "CertificateArtworkUpload": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "upload_url": { + "type": "string", + "format": "uri" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "headers", + "id", + "upload_url" + ] + }, + "CertificateDownload": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "expires_at", + "url" + ] + }, + "CertificateParameters": { + "type": "object", + "properties": { + "version": { + "type": "integer", + "maximum": 1, + "minimum": 1 + }, + "page_width_mm": { + "type": "number", + "format": "double", + "maximum": 420, + "minimum": 100 + }, + "page_height_mm": { + "type": "number", + "format": "double", + "maximum": 420, + "minimum": 100 + }, + "grid_mm": { + "type": "number", + "format": "double", + "maximum": 10, + "minimum": 0.5 + }, + "name_box": { + "$ref": "#/components/schemas/PositionedElement" + } + } + }, + "CertificateRevoke": { + "type": "object", + "properties": { + "reason": { + "$ref": "#/components/schemas/CertificateRevokeReasonEnum" + } + }, + "required": [ + "reason" + ] + }, + "CertificateRevokeReasonEnum": { + "enum": [ + "staff_revoked" + ], + "type": "string", + "description": "* `staff_revoked` - staff_revoked" + }, + "CertificateSummary": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/CertificateSummaryStatusEnum" + } + ], + "readOnly": true + }, + "revision": { + "type": "integer", + "readOnly": true + }, + "issued_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "rendered_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "id", + "issued_at", + "rendered_at", + "revision", + "revoked_at", + "status" + ] + }, + "CertificateSummaryStatusEnum": { + "enum": [ + "pending", + "rendering", + "active", + "failed", + "revoked" + ], + "type": "string", + "description": "* `pending` - Pending\n* `rendering` - Rendering\n* `active` - Active\n* `failed` - Failed\n* `revoked` - Revoked" + }, + "CertificateTemplateRevision": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "nullable": true + }, + "parameters": { + "$ref": "#/components/schemas/CertificateParameters" + }, + "artwork_id": { + "type": "integer", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "required": [ + "artwork_id", + "created_at", + "id", + "parameters" + ] + }, + "CertificateTemplateWrite": { + "type": "object", + "properties": { + "parameters": { + "$ref": "#/components/schemas/CertificateParameters" + }, + "artwork_id": { + "type": "integer", + "minimum": 1, + "nullable": true + } + }, + "required": [ + "parameters" + ] + }, + "CertificationGrant": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "certification_type": { + "type": "integer", + "readOnly": true + }, + "membership": { + "type": "integer", + "readOnly": true + }, + "member_name": { + "type": "string", + "readOnly": true + }, + "granted_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "granted_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "notes": { + "type": "string", + "readOnly": true + }, + "is_live": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "certification_type", + "expires_at", + "granted_at", + "granted_by", + "id", + "is_live", + "member_name", + "membership", + "notes", + "revoked_at", + "revoked_by" + ] + }, + "CertificationGrantCreate": { + "type": "object", + "properties": { + "membership_id": { + "type": "integer" + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "notes": { + "type": "string", + "default": "" + } + }, + "required": [ + "membership_id" + ] + }, + "CertificationGrantPage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CertificationGrant" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "CertificationGrantRevoke": { + "type": "object", + "properties": { + "notes": { + "type": "string", + "default": "" + } + } + }, + "CertificationMember": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "id", + "name" + ] + }, + "CertificationMemberPage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", "nullable": true }, - "current": { + "previous": { + "type": "string", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CertificationMember" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "CertificationType": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "machine_type": { + "type": "integer" + }, + "name": { + "type": "string", + "maxLength": 200 + }, + "description": { + "type": "string" + }, + "validity_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true + }, + "is_required_for_service": { + "type": "boolean" + }, + "is_required_for_booking": { "type": "boolean" + }, + "is_active": { + "type": "boolean", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ "created_at", - "current", - "expires_at", "id", - "kind", - "last_used_at" + "is_active", + "machine_type", + "name", + "updated_at" ] }, - "AccountSessionKindEnum": { - "enum": [ - "web", - "device" - ], - "type": "string", - "description": "* `web` - web\n* `device` - device" - }, - "AccountSessionPage": { + "CertificationTypePage": { "type": "object", "properties": { "count": { @@ -67714,18 +89323,16 @@ }, "next": { "type": "string", - "format": "uri", "nullable": true }, "previous": { "type": "string", - "format": "uri", "nullable": true }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/AccountSession" + "$ref": "#/components/schemas/CertificationType" } } }, @@ -67736,2393 +89343,2163 @@ "results" ] }, - "AccountingJournalReport": { + "ChainStatusEnum": { + "enum": [ + "pending", + "locally_verified", + "invalid", + "unavailable" + ], + "type": "string", + "description": "* `pending` - Pending\n* `locally_verified` - Locally verified\n* `invalid` - Invalid\n* `unavailable` - Unavailable" + }, + "ChallengeRequest": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "token": { + "type": "string", + "maxLength": 256 }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/JournalLine" - } + "public_key": { + "type": "string", + "maxLength": 64 } }, "required": [ - "rows", - "typed_rows" + "public_key", + "token" ] }, - "Ack": { + "ChallengeResponse": { "type": "object", "properties": { - "lease_token": { + "challenge": { + "type": "string" + }, + "expires_at": { "type": "string", - "maxLength": 128, - "minLength": 32 + "format": "date-time" } }, "required": [ - "lease_token" + "challenge", + "expires_at" ] }, - "ActiveLoansReport": { + "ChangePassword": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "current_password": { + "type": "string", + "writeOnly": true }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ActiveLoansReportRow" - } + "new_password": { + "type": "string", + "writeOnly": true } }, "required": [ - "rows", - "typed_rows" + "current_password", + "new_password" ] }, - "ActiveLoansReportRow": { + "ChangePasswordResponse": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "Channel06eEnum": { + "enum": [ + "sms", + "whatsapp" + ], + "type": "string", + "description": "* `sms` - sms\n* `whatsapp` - whatsapp" + }, + "Channel292Enum": { + "enum": [ + "in_app", + "email", + "telegram", + "slack", + "mattermost", + "discord", + "webhook", + "native_push", + "sms", + "whatsapp" + ], + "type": "string", + "description": "* `in_app` - Inbox\n* `email` - Email\n* `telegram` - Telegram\n* `slack` - Slack\n* `mattermost` - Mattermost\n* `discord` - Discord\n* `webhook` - Signed webhook\n* `native_push` - Native push\n* `sms` - SMS\n* `whatsapp` - WhatsApp" + }, + "Channel59dEnum": { + "enum": [ + "telegram", + "slack", + "mattermost", + "discord", + "webhook" + ], + "type": "string", + "description": "* `telegram` - Telegram\n* `slack` - Slack\n* `mattermost` - Mattermost\n* `discord` - Discord\n* `webhook` - Signed webhook" + }, + "ChannelB6fEnum": { + "enum": [ + "email", + "in_app", + "native_push", + "sms", + "whatsapp" + ], + "type": "string", + "description": "* `email` - email\n* `in_app` - in_app\n* `native_push` - native_push\n* `sms` - sms\n* `whatsapp` - whatsapp" + }, + "ChannelsEnum": { + "enum": [ + "email", + "telegram", + "slack", + "mattermost", + "discord", + "native_push", + "webhook", + "sms", + "whatsapp" + ], + "type": "string", + "description": "* `email` - email\n* `telegram` - telegram\n* `slack` - slack\n* `mattermost` - mattermost\n* `discord` - discord\n* `native_push` - native_push\n* `webhook` - webhook\n* `sms` - sms\n* `whatsapp` - whatsapp" + }, + "CheckoutUrl": { + "type": "object", + "properties": { + "checkout_url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "checkout_url" + ] + }, + "ClaimRedemption": { + "type": "object", + "properties": { + "code": { + "type": "string", + "writeOnly": true, + "maxLength": 64 + }, + "makerspace_slug": { + "type": "string", + "writeOnly": true, + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" + } + }, + "required": [ + "code", + "makerspace_slug" + ] + }, + "ClaimRedemptionResponse": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/AuthUserPayload" }, + "access": { + "type": "string" + } + }, + "required": [ + "access", + "user" + ] + }, + "ClaimableInvitation": { + "type": "object", + "properties": { "id": { "type": "integer" }, - "requester": { - "type": "string" + "makerspace": { + "type": "object", + "additionalProperties": {} }, - "status": { - "type": "string" + "inviter": { + "type": "string", + "nullable": true }, - "issued_at": { + "auto_activates": { + "type": "boolean" + }, + "is_staff": { + "type": "boolean" + }, + "role": { "type": "string", - "format": "date-time", "nullable": true } }, "required": [ + "auto_activates", "id", - "issued_at", - "requester", - "status" + "inviter", + "is_staff", + "makerspace", + "role" ] }, - "AddressingStyleEnum": { + "ClientPlatformEnum": { "enum": [ - "auto", - "path", - "virtual" + "web", + "ios", + "android" ], "type": "string", - "description": "* `auto` - Auto\n* `path` - Path\n* `virtual` - Virtual host" + "description": "* `web` - Web\n* `ios` - iOS\n* `android` - Android" }, - "AdminMembership": { + "ClientTypeEnum": { + "enum": [ + "browser", + "server" + ], + "type": "string", + "description": "* `browser` - Browser\n* `server` - Server" + }, + "ClosureApproval": { "type": "object", "properties": { "id": { - "type": "integer", + "type": "string", + "format": "uuid", "readOnly": true }, - "status": { - "$ref": "#/components/schemas/Status37fEnum" - }, - "user": { - "allOf": [ - { - "$ref": "#/components/schemas/AdminMembershipUser" - } - ], - "readOnly": true + "closure_digest": { + "type": "string", + "maxLength": 64 }, - "assigned_role": { - "allOf": [ - { - "$ref": "#/components/schemas/AdminMembershipRole" - } - ], - "nullable": true, + "identity_count": { + "type": "string", "readOnly": true }, - "can_refer": { - "type": "boolean" - }, - "can_verify": { - "type": "boolean" - }, - "verified_at": { + "approved_count": { "type": "string", - "format": "date-time", - "nullable": true + "readOnly": true }, - "activated_at": { + "approved_at": { "type": "string", "format": "date-time", - "nullable": true + "readOnly": true }, "revoked_at": { "type": "string", "format": "date-time", "nullable": true - }, - "revocation_reason": { - "type": "string" - }, - "waiver_accepted_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "waiver_version_accepted": { + } + }, + "required": [ + "approved_at", + "approved_count", + "closure_digest", + "id", + "identity_count" + ] + }, + "ClosureApprovalCreate": { + "type": "object", + "properties": { + "digest": { "type": "string", - "nullable": true, - "maxLength": 64 - }, - "waiver_current": { - "type": "boolean", - "readOnly": true - }, - "waiver_required": { - "type": "boolean", - "readOnly": true - }, - "payment": { - "allOf": [ - { - "$ref": "#/components/schemas/StaffPaymentSummary" - } - ], - "nullable": true, - "readOnly": true + "maxLength": 64, + "minLength": 64 }, - "vouches": { + "decisions": { "type": "array", "items": { - "$ref": "#/components/schemas/Vouch" - }, - "readOnly": true + "$ref": "#/components/schemas/IdentityDisclosureDecision" + } } }, "required": [ - "assigned_role", - "id", - "payment", - "user", - "vouches", - "waiver_current", - "waiver_required" + "decisions", + "digest" ] }, - "AdminMembershipRole": { + "ClosureIdentity": { "type": "object", "properties": { "id": { "type": "integer" }, - "name": { + "username": { "type": "string" }, - "slug": { + "email": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "first_name": { + "type": "string" + }, + "last_name": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "phone": { "type": "string" + }, + "date_joined": { + "type": "string", + "format": "date-time" } }, "required": [ + "date_joined", + "display_name", + "email", + "first_name", "id", - "name", - "slug" + "last_name", + "phone", + "username" ] }, - "AdminMembershipUser": { + "CollaborativeEvent": { "type": "object", "properties": { - "id": { - "type": "integer" + "participating_organizations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ParticipatingOrganization" + }, + "readOnly": true }, - "username": { - "type": "string" + "audience_label": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "registration_audience": { + "allOf": [ + { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + } + ], + "readOnly": true + }, + "category": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "readOnly": true + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "ticket_types": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "audience": { + "type": "string" + }, + "spots_left": { + "type": "integer", + "nullable": true + } + } + }, + "readOnly": true }, - "email": { - "type": "string", - "format": "email" + "promo_codes_enabled": { + "type": "boolean", + "readOnly": true }, - "display_name": { - "type": "string" - } - }, - "required": [ - "display_name", - "email", - "id", - "username" - ] - }, - "AdminRequest": { - "type": "object", - "properties": { - "has_kit_checklist": { + "ticket_selection_required": { "type": "boolean", "readOnly": true }, - "id": { - "type": "integer", + "show_registration_count": { + "type": "boolean", "readOnly": true }, - "makerspace_id": { + "registration_count": { "type": "integer", + "nullable": true, "readOnly": true }, - "requester_username": { + "payment_amount": { "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "requester_name": { + "currency": { "type": "string", "readOnly": true }, - "requester_display": { - "type": "string", + "id": { + "type": "integer", "readOnly": true }, - "requester_contact_email": { + "title": { "type": "string", - "format": "email", "readOnly": true }, - "requester_contact_phone": { + "description": { "type": "string", "readOnly": true }, - "requester_contact_verified": { - "type": "boolean", - "readOnly": true - }, - "status": { + "starts_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "requested_for": { + "ends_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "rejection_reason": { + "location": { "type": "string", "readOnly": true }, - "assigned_box_label": { - "type": "string", - "readOnly": true, - "nullable": true - }, - "accepted_by": { + "location_kind": { "allOf": [ { - "$ref": "#/components/schemas/AdminRequestActor" + "$ref": "#/components/schemas/LocationKindEnum" } ], + "readOnly": true + }, + "custom_form": { "readOnly": true, "nullable": true }, - "issued_by": { + "capacity": { + "type": "integer", + "minimum": 0, + "readOnly": true + }, + "availability": { "allOf": [ { - "$ref": "#/components/schemas/AdminRequestActor" + "$ref": "#/components/schemas/AvailabilityEnum" } ], - "readOnly": true, - "nullable": true - }, - "accepted_at": { - "type": "string", - "format": "date-time", "readOnly": true }, - "issued_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "return_due_at": { - "type": "string", - "format": "date-time", + "registration_requires_approval": { + "type": "boolean", "readOnly": true }, - "return_reminder_sent_at": { + "effective_registration_cutoff_at": { "type": "string", "format": "date-time", + "nullable": true, "readOnly": true }, - "renewal_count": { - "type": "integer", + "registration_open": { + "type": "boolean", "readOnly": true }, - "closed_at": { + "image_url": { "type": "string", - "format": "date-time", + "format": "uri", + "nullable": true, "readOnly": true }, - "created_at": { + "host_name": { "type": "string", - "format": "date-time", "readOnly": true }, - "updated_at": { + "host_slug": { "type": "string", - "format": "date-time", - "readOnly": true - }, - "issue_evidence_id": { - "type": "integer", "readOnly": true, - "nullable": true + "pattern": "^[-a-zA-Z0-9_]+$" }, - "return_evidence_ids": { - "type": "array", - "items": {}, + "host_waiver": { + "allOf": [ + { + "$ref": "#/components/schemas/HostWaiver" + } + ], + "nullable": true, "readOnly": true }, - "items": { + "consent_repair_required": { + "type": "boolean", + "readOnly": true + }, + "organizers": { "type": "array", "items": { - "$ref": "#/components/schemas/AdminRequestItem" + "$ref": "#/components/schemas/EventOrganizerSummary" }, "readOnly": true - } - }, - "required": [ - "accepted_at", - "accepted_by", - "assigned_box_label", - "closed_at", - "created_at", - "has_kit_checklist", - "id", - "issue_evidence_id", - "issued_at", - "issued_by", - "items", - "makerspace_id", - "rejection_reason", - "renewal_count", - "requested_for", - "requester_contact_email", - "requester_contact_phone", - "requester_contact_verified", - "requester_display", - "requester_name", - "requester_username", - "return_due_at", - "return_evidence_ids", - "return_reminder_sent_at", - "status", - "updated_at" - ] - }, - "AdminRequestActor": { - "type": "object", - "properties": { - "username": { - "type": "string", - "readOnly": true }, - "role": { - "type": "string", + "series": { + "type": "object", + "nullable": true, "readOnly": true } }, "required": [ - "role", - "username" + "audience_label", + "availability", + "capacity", + "category", + "consent_repair_required", + "currency", + "custom_form", + "description", + "effective_registration_cutoff_at", + "ends_at", + "host_name", + "host_slug", + "host_waiver", + "id", + "image_url", + "location", + "location_kind", + "organizers", + "participating_organizations", + "payment_amount", + "promo_codes_enabled", + "registration_audience", + "registration_count", + "registration_open", + "registration_requires_approval", + "series", + "show_registration_count", + "starts_at", + "tags", + "ticket_selection_required", + "ticket_types", + "title" ] }, - "AdminRequestItem": { + "CollaborativeEventRegistrationInput": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "custom_answers": { + "writeOnly": true, + "nullable": true }, - "product_id": { + "host_waiver_id": { "type": "integer", - "readOnly": true - }, - "product_name": { - "type": "string", - "readOnly": true - }, - "storage_location": { - "type": "string", - "readOnly": true + "minimum": 1, + "nullable": true }, - "tracking_mode": { + "host_waiver_version": { "type": "string", - "readOnly": true + "nullable": true, + "maxLength": 64 }, - "requires_asset_qr": { + "host_waiver_accepted": { "type": "boolean", - "readOnly": true - }, - "requested_quantity": { - "type": "integer", - "readOnly": true - }, - "accepted_quantity": { - "type": "integer", - "readOnly": true - }, - "issued_quantity": { - "type": "integer", - "readOnly": true + "default": false }, - "returned_quantity": { - "type": "integer", - "readOnly": true + "phone": { + "type": "string", + "writeOnly": true, + "maxLength": 64 }, - "damaged_quantity": { - "type": "integer", - "readOnly": true + "email": { + "writeOnly": true, + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "missing_quantity": { - "type": "integer", - "readOnly": true + "invite_code": { + "type": "string", + "writeOnly": true, + "maxLength": 128 }, - "needs_fix_quantity": { + "ticket_type_id": { "type": "integer", - "readOnly": true + "minimum": 1, + "writeOnly": true }, - "issued_assets": { - "type": "array", - "items": {}, - "readOnly": true - } - }, - "required": [ - "accepted_quantity", - "damaged_quantity", - "id", - "issued_assets", - "issued_quantity", - "missing_quantity", - "needs_fix_quantity", - "product_id", - "product_name", - "requested_quantity", - "requires_asset_qr", - "returned_quantity", - "storage_location", - "tracking_mode" - ] - }, - "AdmissionAnswers": { - "type": "object", - "properties": { - "application_answers": { - "nullable": true + "promo_code": { + "type": "string", + "writeOnly": true, + "maxLength": 128 } } }, - "AdmissionExport": { + "CommandAck": { "type": "object", "properties": { - "requests": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AdmissionRequest" - } + "id": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string" + }, + "acknowledged_at": { + "type": "string", + "format": "date-time", + "nullable": true } }, "required": [ - "requests" + "acknowledged_at", + "id", + "status" ] }, - "AdmissionPayment": { + "CommandLease": { "type": "object", "properties": { - "id": { - "type": "integer" + "command_id": { + "type": "string", + "format": "uuid" }, - "status": { + "kind": { "type": "string" }, - "amount": { + "lease_token": { + "type": "string" + }, + "lease_expires_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "date-time" }, - "currency": { + "printer_job_id": { + "type": "string", + "format": "uuid" + }, + "external_job_id": { "type": "string" + }, + "service_request_id": { + "type": "integer" + }, + "label_command_id": { + "type": "string", + "format": "uuid" + }, + "qr_batch_id": { + "type": "integer" + }, + "item_count": { + "type": "integer" } }, "required": [ - "amount", - "currency", - "id", - "status" + "command_id", + "kind", + "lease_expires_at", + "lease_token" ] }, - "AdmissionRequest": { + "CommunicationsHealthReport": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "state": { - "$ref": "#/components/schemas/State9efEnum" - }, - "application_answers": { - "nullable": true, - "readOnly": true + "report_key": { + "type": "string" }, - "vouches": { + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/Vouch" + "type": "array", + "items": {} } }, - "admission_payment": { - "allOf": [ - { - "$ref": "#/components/schemas/AdmissionPayment" - } - ], - "nullable": true + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CommunicationsHealthRow" + } }, - "expires_at": { - "type": "string", - "format": "date-time", - "nullable": true + "meta": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "admission_payment", - "application_answers", - "id", - "state", - "vouches" + "rows", + "typed_rows" ] }, - "AdmissionSettings": { + "CommunicationsHealthRow": { "type": "object", "properties": { - "configured": { - "type": "boolean", - "readOnly": true + "triggered_count": { + "type": "integer", + "minimum": 0 }, - "join_mode": { - "$ref": "#/components/schemas/JoinModeEnum" + "delivery_count": { + "type": "integer", + "minimum": 0 }, - "vouches_required": { + "attempt_count": { "type": "integer", - "maximum": 5, - "minimum": 1 + "minimum": 0 }, - "voucher_min_tenure_days": { + "sent_count": { "type": "integer", - "maximum": 3650, "minimum": 0 }, - "voucher_pending_limit": { + "failed_count": { "type": "integer", - "maximum": 100, - "minimum": 1 + "minimum": 0 }, - "vouch_expiry_days": { + "skipped_count": { "type": "integer", - "maximum": 365, - "minimum": 1 + "minimum": 0 }, - "vouch_replaces_approval": { - "type": "boolean" + "suppressed_count": { + "type": "integer", + "minimum": 0 }, - "payment_timing": { - "$ref": "#/components/schemas/PaymentTimingEnum" + "bounced_count": { + "type": "integer", + "minimum": 0 }, - "application_schema": { - "nullable": true + "complained_count": { + "type": "integer", + "minimum": 0 }, - "default_plan": { + "invalid_count": { "type": "integer", - "nullable": true + "minimum": 0 }, - "joining_fee": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "inbox_created_count": { + "type": "integer", + "minimum": 0 }, - "joining_fee_currency": { - "type": "string", - "pattern": "^[A-Za-z]{3}$" + "inbox_read_count": { + "type": "integer", + "minimum": 0 }, - "rejoin_dues_rule": { - "$ref": "#/components/schemas/RejoinDuesRuleEnum" + "unread_count": { + "type": "integer", + "minimum": 0 }, - "request_expiry_days": { + "accepted_count": { "type": "integer", - "maximum": 365, - "minimum": 1 + "minimum": 0 + }, + "delivered_count": { + "type": "integer", + "minimum": 0 + }, + "read_count": { + "type": "integer", + "minimum": 0 + }, + "pending_count": { + "type": "integer", + "minimum": 0 + }, + "sending_count": { + "type": "integer", + "minimum": 0 + }, + "accepted_only_count": { + "type": "integer", + "minimum": 0 + }, + "sent_only_count": { + "type": "integer", + "minimum": 0 }, - "recipe": { - "allOf": [ - { - "$ref": "#/components/schemas/RecipeEnum" - } - ], - "writeOnly": true - } - }, - "required": [ - "configured" - ] - }, - "AdmissionVouchOutcome": { - "type": "object", - "description": "A voucher receives the outcome, never the applicant's private submission.", - "properties": { - "id": { + "delivered_only_count": { "type": "integer", - "readOnly": true + "minimum": 0 }, - "state": { - "$ref": "#/components/schemas/State9efEnum" - } - }, - "required": [ - "id", - "state" - ] - }, - "AllowedFormatsEnum": { - "enum": [ - "csv", - "xlsx" - ], - "type": "string", - "description": "* `csv` - csv\n* `xlsx` - xlsx" - }, - "AnalyticsReportResponse": { - "oneOf": [ - { - "$ref": "#/components/schemas/AnalyticsSummary" + "estimated_segments": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/TakenItemsReport" + "billed_segments": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/ActiveLoansReport" + "segments": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/ReturnsReport" + "abandoned_webhook_count": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/DamagedMissingReport" + "delivery_rate_numerator": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/DamagedLostReport" + "delivery_rate_denominator": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/QrScansReport" + "read_rate_numerator": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/MostLentReport" + "read_rate_denominator": { + "type": "integer", + "minimum": 0 }, - { - "$ref": "#/components/schemas/TopBorrowersReport" + "makerspace_id": { + "type": "integer" }, - { - "$ref": "#/components/schemas/RecentlyAddedReport" + "period": { + "type": "string", + "format": "date", + "nullable": true, + "description": "Null for the current consent snapshot." }, - { - "$ref": "#/components/schemas/MachineUsageReport" + "module_key": { + "type": "string" }, - { - "$ref": "#/components/schemas/EventAttendanceReport" + "channel": { + "type": "string" }, - { - "$ref": "#/components/schemas/BookingUtilizationReport" + "feature": { + "type": "string" }, - { - "$ref": "#/components/schemas/MaintenanceActivityReport" + "provider_source": { + "type": "string" }, - { - "$ref": "#/components/schemas/MemberActivityReport" + "platform": { + "type": "string" }, - { - "$ref": "#/components/schemas/FabLabHealthReport" + "audience": { + "type": "string" }, - { - "$ref": "#/components/schemas/PaymentReconciliationReport" + "skip_reason": { + "type": "string" }, - { - "$ref": "#/components/schemas/PaymentCashBook" + "segment_source": { + "$ref": "#/components/schemas/SegmentSourceEnum" }, - { - "$ref": "#/components/schemas/PaymentsLedgerReport" + "measurement": { + "$ref": "#/components/schemas/CommunicationsHealthRowMeasurementEnum" }, - { - "$ref": "#/components/schemas/RevenueOverTimeReport" + "success_rate_percent": { + "type": "number", + "format": "double", + "nullable": true }, - { - "$ref": "#/components/schemas/CashBookReport" + "read_rate_percent": { + "type": "number", + "format": "double", + "nullable": true }, - { - "$ref": "#/components/schemas/AccountingJournalReport" + "delivery_rate": { + "type": "number", + "format": "double", + "nullable": true, + "description": "Delivered / accepted; null for a zero denominator." }, - { - "$ref": "#/components/schemas/GenericAnalyticsReport" + "read_rate": { + "type": "number", + "format": "double", + "nullable": true, + "description": "Read / delivered; null for a zero denominator." }, - { - "$ref": "#/components/schemas/MemberRetentionReport" + "consented_member_count": { + "type": "integer", + "nullable": true, + "description": "Current live consent among active local members; excluded from organization totals." }, - { - "$ref": "#/components/schemas/MembershipRevenueReport" + "last_activity_at": { + "type": "string", + "format": "date-time", + "nullable": true } - ] + }, + "required": [ + "abandoned_webhook_count", + "accepted_count", + "accepted_only_count", + "attempt_count", + "audience", + "billed_segments", + "bounced_count", + "channel", + "complained_count", + "consented_member_count", + "delivered_count", + "delivered_only_count", + "delivery_count", + "delivery_rate", + "delivery_rate_denominator", + "delivery_rate_numerator", + "estimated_segments", + "failed_count", + "feature", + "inbox_created_count", + "inbox_read_count", + "invalid_count", + "last_activity_at", + "measurement", + "module_key", + "pending_count", + "period", + "platform", + "provider_source", + "read_count", + "read_rate", + "read_rate_denominator", + "read_rate_numerator", + "read_rate_percent", + "segment_source", + "segments", + "sending_count", + "sent_count", + "sent_only_count", + "skip_reason", + "skipped_count", + "success_rate_percent", + "suppressed_count", + "triggered_count", + "unread_count" + ] + }, + "CommunicationsHealthRowMeasurementEnum": { + "enum": [ + "delivery_cohort", + "webhook_receipts", + "current_consent_snapshot" + ], + "type": "string", + "description": "* `delivery_cohort` - delivery_cohort\n* `webhook_receipts` - webhook_receipts\n* `current_consent_snapshot` - current_consent_snapshot" }, - "AnalyticsSummary": { + "CompleteRequest": { "type": "object", "properties": { - "products": { - "type": "integer" - }, - "assets": { - "type": "integer" - }, - "active_loans": { - "type": "integer" - }, - "available_quantity": { - "type": "integer" + "token": { + "type": "string", + "maxLength": 256 }, - "issued_quantity": { - "type": "integer" + "public_key": { + "type": "string", + "maxLength": 64 }, - "damaged_quantity": { - "type": "integer" + "challenge": { + "type": "string", + "maxLength": 64 }, - "missing_quantity": { - "type": "integer" + "signature": { + "type": "string", + "maxLength": 128 } }, "required": [ - "active_loans", - "assets", - "available_quantity", - "damaged_quantity", - "issued_quantity", - "missing_quantity", - "products" + "challenge", + "public_key", + "signature", + "token" ] }, - "AnchorEnum": { + "ConditionEnum": { "enum": [ - "completion", - "calendar" + "available", + "damaged", + "lost", + "unknown" ], "type": "string", - "description": "* `completion` - Completion date\n* `calendar` - Fixed calendar" + "description": "* `available` - Available\n* `damaged` - Damaged\n* `lost` - Lost\n* `unknown` - Unknown" }, - "AnchorStatusEnum": { + "ConfirmEnum": { + "enum": [ + "DELETE" + ], + "type": "string", + "description": "* `DELETE` - DELETE" + }, + "ConnectStatusEnum": { "enum": [ + "unconnected", "pending", - "not_configured", - "externally_anchored", - "invalid", - "unavailable" + "active", + "restricted", + "disconnected" ], "type": "string", - "description": "* `pending` - Pending\n* `not_configured` - Not configured\n* `externally_anchored` - Externally anchored\n* `invalid` - Invalid\n* `unavailable` - Unavailable" + "description": "* `unconnected` - Unconnected\n* `pending` - Pending\n* `active` - Active\n* `restricted` - Restricted\n* `disconnected` - Disconnected" }, - "ApiClient": { + "ConnectedDeviceIdentity": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "label": { - "type": "string", - "maxLength": 200 - }, - "client_id": { - "type": "string", - "readOnly": true - }, - "client_type": { - "$ref": "#/components/schemas/ClientTypeEnum" - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "rate_limit_tier": { - "$ref": "#/components/schemas/RateLimitTierEnum" - }, - "makerspace": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "public_makerspace_code": { + "device_id": { "type": "string", - "readOnly": true - }, - "allowed_origins": { - "type": "array", - "items": { - "type": "string" - } + "format": "uuid" }, - "backend_base_url": { - "type": "string", - "readOnly": true + "makerspace_id": { + "type": "integer" }, - "public_api_base_url": { - "type": "string", - "readOnly": true + "status": { + "type": "string" }, - "is_active": { - "type": "boolean" + "key_version": { + "type": "integer" }, "last_seen_at": { "type": "string", "format": "date-time", - "readOnly": true, - "nullable": true - }, - "last_seen_ip": { - "type": "string", - "readOnly": true, "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true } }, "required": [ - "allowed_origins", - "backend_base_url", - "client_id", - "created_at", - "id", - "label", + "device_id", + "key_version", "last_seen_at", - "last_seen_ip", - "makerspace", - "public_api_base_url", - "public_makerspace_code", - "scopes", - "updated_at" + "makerspace_id", + "status" ] }, - "ApiClientCreateResponse": { + "ConsentWrite": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "label": { - "type": "string", - "maxLength": 200 - }, - "client_id": { - "type": "string", - "readOnly": true - }, - "client_secret": { - "type": "string", - "readOnly": true - }, - "client_type": { - "$ref": "#/components/schemas/ClientTypeEnum" - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "rate_limit_tier": { - "$ref": "#/components/schemas/RateLimitTierEnum" - }, "makerspace": { "type": "integer", - "readOnly": true, - "nullable": true - }, - "public_makerspace_code": { - "type": "string", - "readOnly": true - }, - "allowed_origins": { - "type": "array", - "items": { - "type": "string" - } + "minimum": 1 }, - "backend_base_url": { - "type": "string", - "readOnly": true + "channel": { + "$ref": "#/components/schemas/Channel06eEnum" }, - "public_api_base_url": { - "type": "string", + "action": { + "$ref": "#/components/schemas/ConsentWriteActionEnum" + } + }, + "required": [ + "action", + "channel", + "makerspace" + ] + }, + "ConsentWriteActionEnum": { + "enum": [ + "opted_in", + "opted_out" + ], + "type": "string", + "description": "* `opted_in` - opted_in\n* `opted_out` - opted_out" + }, + "ConsumableCandidate": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "is_active": { - "type": "boolean" - }, - "last_seen_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "last_seen_ip": { - "type": "string", - "readOnly": true, - "nullable": true - }, - "created_at": { + "name": { "type": "string", - "format": "date-time", "readOnly": true }, - "updated_at": { - "type": "string", - "format": "date-time", + "available": { + "type": "integer", "readOnly": true } }, "required": [ - "allowed_origins", - "backend_base_url", - "client_id", - "client_secret", - "created_at", + "available", "id", - "label", - "last_seen_at", - "last_seen_ip", - "makerspace", - "public_api_base_url", - "public_makerspace_code", - "scopes", - "updated_at" + "name" ] }, - "ApiClientScopeCatalogResponse": { + "Consumption": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true + "machine_consumable_id": { + "type": "integer", + "minimum": 1 }, - "previous": { + "quantity": { "type": "string", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ApiClientScopeOption" - } + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" } }, "required": [ - "count", - "results" + "machine_consumable_id", + "quantity" ] }, - "ApiClientScopeOption": { + "ContainerAssetSummary": { "type": "object", "properties": { - "value": { - "type": "string" + "id": { + "type": "integer" }, - "label": { + "asset_tag": { "type": "string" }, - "description": { + "product": { "type": "string" }, - "group": { + "status": { "type": "string" + } + }, + "required": [ + "asset_tag", + "id", + "product", + "status" + ] + }, + "ContainerContents": { + "type": "object", + "properties": { + "container": { + "$ref": "#/components/schemas/Box" }, - "grantable": { - "type": "boolean" + "products": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContainerProductSummary" + } }, - "lock_reason": { - "type": "string", - "nullable": true + "assets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContainerAssetSummary" + } + }, + "children": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Box" + } } }, "required": [ - "description", - "grantable", - "group", - "label", - "lock_reason", - "value" + "assets", + "children", + "container", + "products" ] }, - "ApiIntegrationSettings": { + "ContainerHistory": { "type": "object", "properties": { - "public_code": { - "type": "string", - "readOnly": true - }, - "public_api_key": { - "type": "string", - "readOnly": true + "container": { + "type": "integer" }, - "cors_allowed_origins": { + "scans": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ContainerScanHistoryItem" + } + } + }, + "required": [ + "container", + "scans" + ] + }, + "ContainerMove": { + "type": "object", + "properties": { + "kit_components": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/KitComponent" }, - "readOnly": true - }, - "telegram_group_chat_id": { - "type": "string", - "maxLength": 64 - }, - "telegram_bot_token": { - "type": "string", - "writeOnly": true - }, - "telegram_bot_token_set": { - "type": "boolean", - "readOnly": true - }, - "smtp_host": { - "type": "string", - "maxLength": 200 + "maxItems": 50 }, - "smtp_port": { + "parent_id": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "smtp_username": { - "type": "string", - "maxLength": 200 + "nullable": true }, - "smtp_password": { - "type": "string", - "writeOnly": true + "label": { + "type": "string" }, - "smtp_password_set": { - "type": "boolean", - "readOnly": true + "location": { + "type": "string" }, - "smtp_use_tls": { - "type": "boolean" + "description": { + "type": "string" }, - "smtp_use_ssl": { + "is_active": { "type": "boolean" + } + } + }, + "ContainerProductSummary": { + "type": "object", + "properties": { + "id": { + "type": "integer" }, - "smtp_from_email": { - "oneOf": [ - { - "type": "string", - "format": "email", - "maxLength": 254 - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "slack_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 - }, - "slack_webhook_url_set": { - "type": "boolean", - "readOnly": true - }, - "mattermost_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 - }, - "mattermost_webhook_url_set": { - "type": "boolean", - "readOnly": true - }, - "discord_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 + "name": { + "type": "string" }, - "discord_webhook_url_set": { - "type": "boolean", - "readOnly": true + "available_quantity": { + "type": "integer" }, - "default_loan_days": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "tracking_mode": { + "type": "string" } }, "required": [ - "cors_allowed_origins", - "discord_webhook_url_set", - "mattermost_webhook_url_set", - "public_api_key", - "public_code", - "slack_webhook_url_set", - "smtp_password_set", - "telegram_bot_token_set" + "available_quantity", + "id", + "name", + "tracking_mode" ] }, - "ApiKeyRequest": { + "ContainerScanHistoryItem": { "type": "object", "properties": { "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer" - }, - "label": { - "type": "string", - "maxLength": 120 - }, - "reason": { "type": "string" }, - "allowed_origins": { - "type": "array", - "items": { - "type": "string" - } + "source": { + "type": "string" }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusE94Enum" - } - ], - "readOnly": true + "context": { + "type": "string" }, - "resolution_note": { - "type": "string", - "readOnly": true + "actor": { + "type": "integer", + "nullable": true }, "created_at": { "type": "string", - "format": "date-time", - "readOnly": true - }, - "resolved_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "format": "date-time" } }, "required": [ - "allowed_origins", + "actor", + "context", "created_at", "id", - "label", - "makerspace", - "resolution_note", - "resolved_at", - "status" + "source" ] }, - "ApprovalModeE35Enum": { + "ContentTypeEnum": { "enum": [ - "instant", - "approve" + "image/png", + "image/jpeg" ], "type": "string", - "description": "* `instant` - Instant confirmation\n* `approve` - Staff approval required" + "description": "* `image/png` - image/png\n* `image/jpeg` - image/jpeg" }, - "ApproveRequest": { + "ContextEnum": { + "enum": [ + "issue", + "return", + "inventory_check" + ], + "type": "string", + "description": "* `issue` - issue\n* `return` - return\n* `inventory_check` - inventory_check" + }, + "Count": { "type": "object", "properties": { - "role_id": { + "count": { "type": "integer" - }, - "plan_id": { - "type": "integer", - "nullable": true } }, "required": [ - "role_id" + "count" ] }, - "ArchiveCustodyReadiness": { + "CreateBoxQr": { "type": "object", "properties": { - "below_floor_makerspaces": { - "type": "integer", - "minimum": 0 + "makerspace_id": { + "type": "integer" }, - "zero_recipient_makerspaces": { - "type": "integer", - "minimum": 0 + "label": { + "type": "string" }, - "undelivered_alarms": { - "type": "integer", - "minimum": 0 + "location": { + "type": "string" }, - "alarms_with_no_operator_address": { + "description": { + "type": "string" + }, + "parent_id": { "type": "integer", - "minimum": 0 + "nullable": true } }, "required": [ - "alarms_with_no_operator_address", - "below_floor_makerspaces", - "undelivered_alarms", - "zero_recipient_makerspaces" - ] - }, - "ArchiveCustodyStateEnum": { - "type": "string", - "enum": [ - "healthy", - "not_applicable", - "degraded_one_recipient", - "floor_breached_zero" + "label", + "makerspace_id" ] }, - "ArchiveRecipient": { + "CreateToolQr": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "makerspace_id": { + "type": "integer" }, - "public_recipient": { - "type": "string", - "readOnly": true + "product_id": { + "type": "integer" }, - "fingerprint": { - "type": "string", - "readOnly": true + "asset_id": { + "type": "integer" + } + }, + "required": [ + "makerspace_id" + ] + }, + "CreationInviteDelivery": { + "type": "object", + "properties": { + "accept_url": { + "type": "string" }, - "label": { + "delivery_status": { + "type": "string" + } + }, + "required": [ + "accept_url", + "delivery_status" + ] + }, + "CreationProfile": { + "type": "object", + "description": "Explicit fields override a pack; omitted fields keep its configuration.", + "properties": { + "timezone": { "type": "string", - "readOnly": true - }, - "added_by": { - "type": "integer", - "readOnly": true, - "nullable": true + "maxLength": 100 }, - "added_at": { + "country": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 2 }, - "revoked_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "opening_hours": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "required": [ + "weekly", + "exceptions" + ], + "additionalProperties": false, + "properties": { + "weekly": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + "sun" + ], + "properties": { + "mon": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "tue": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "wed": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "thu": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "fri": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sat": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sun": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + } + } + } + ] + }, + "exceptions": { + "type": "object", + "maxProperties": 366, + "additionalProperties": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." + } + } + } + ], + "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." }, - "compromised_at": { + "default_currency": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "maxLength": 3 }, - "verified_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "timezone_choices": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true }, - "challenge_issued_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "country_choices": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true } }, "required": [ - "added_at", - "added_by", - "challenge_issued_at", - "compromised_at", - "fingerprint", - "id", - "label", - "public_recipient", - "revoked_at", - "verified_at" + "country_choices", + "timezone_choices" ] }, - "ArchiveRecipientChallenge": { + "CustodyState": { "type": "object", "properties": { - "recipient": { - "$ref": "#/components/schemas/ArchiveRecipient" + "state": { + "type": "string" }, - "encrypted_challenge": { - "type": "string", - "description": "The binary age ciphertext encoded as unpadded base64url for JSON transport. Its decrypted plaintext is the 32-byte nonce encoded as canonical, unpadded base64url." + "reason_code": { + "type": "string" }, - "nonce_encoding": { - "type": "string", - "default": "base64url-unpadded", - "description": "The decrypted nonce uses canonical unpadded base64url." - } - }, - "required": [ - "encrypted_challenge", - "recipient" - ] - }, - "ArchiveRecipientCreate": { - "type": "object", - "properties": { - "public_recipient": { + "entered_at": { "type": "string", - "maxLength": 200 + "format": "date-time", + "nullable": true }, - "label": { + "cleared_at": { "type": "string", - "maxLength": 120 + "format": "date-time", + "nullable": true } }, "required": [ - "label", - "public_recipient" + "cleared_at", + "entered_at", + "reason_code", + "state" ] }, - "ArchiveRecipientError": { + "CutoverOutcome": { "type": "object", "properties": { - "detail": { + "message": { "type": "string" }, - "code": { - "type": "string" - } - }, - "required": [ - "code", - "detail" - ] - }, - "ArchiveRecipientVerify": { - "type": "object", - "properties": { - "nonce": { - "type": "string", - "description": "The decrypted 32-byte nonce in canonical, unpadded base64url form. Padding and non-canonical encodings are refused.", - "maxLength": 128 + "receipt": { + "$ref": "#/components/schemas/ReceiptEnvelope" } }, "required": [ - "nonce" + "message" ] }, - "ArchiveRequestError": { + "CutoverReceiptRequest": { "type": "object", "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" + "receipt": { + "$ref": "#/components/schemas/ReceiptEnvelope" } }, "required": [ - "detail" + "receipt" ] }, - "ArchiveRequestValidationError": { + "DamagedLostReport": { "type": "object", - "description": "DRF field-keyed errors, which are NOT the `detail`/`code` shape.\n\nA blank or overlong reason fails in `serializer.is_valid(raise_exception=True)` and comes\nback as `{\"reason\": [\"...\"]}`. Declaring every 400 as `ArchiveRequestError` published a\ncontract the endpoint does not honour, so a generated client would destructure `detail`\nand find nothing. Follows the `ProvisionSubdomainValidationErrorSerializer` precedent.", "properties": { - "reason": { + "rows": { "type": "array", "items": { - "type": "string" + "type": "array", + "items": {} } - } - } - }, - "ArchivedPaymentMakerspace": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "slug": { - "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "name": { - "type": "string" - } - }, - "required": [ - "id", - "name", - "slug" - ] - }, - "ArchivedPaymentSummary": { - "type": "object", - "properties": { - "makerspace": { - "$ref": "#/components/schemas/ArchivedPaymentMakerspace" - }, - "pending_count": { - "type": "integer", - "minimum": 0 - }, - "total_count": { - "type": "integer", - "minimum": 1 - } - }, - "required": [ - "makerspace", - "pending_count", - "total_count" - ] - }, - "AssetChainGroup": { - "type": "object", - "properties": { - "asset_id": { - "type": "integer", - "nullable": true - }, - "asset_tag": { - "type": "string" }, - "serial_number": { - "type": "string" - }, - "status": { - "type": "string" - }, - "events": { + "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/TimelineEvent" + "$ref": "#/components/schemas/DamagedLostReportRow" } } }, "required": [ - "asset_id", - "asset_tag", - "events", - "serial_number", - "status" + "rows", + "typed_rows" ] }, - "AssetGenerate": { + "DamagedLostReportRow": { "type": "object", "properties": { - "count": { - "type": "integer", - "maximum": 200, - "minimum": 1 + "makerspace_id": { + "type": "integer" }, - "name_prefix": { + "product_id": { + "type": "integer" + }, + "product_name": { "type": "string" }, - "serial_numbers": { - "type": "array", - "items": { - "type": "string" - } + "damaged_quantity": { + "type": "integer" }, - "print_batch_id": { - "type": "integer", - "nullable": true + "lost_quantity": { + "type": "integer" }, - "create_print_batch": { - "type": "boolean", - "default": false - } - }, - "required": [ - "count" - ] - }, - "AssetGenerateItem": { - "type": "object", - "properties": { - "id": { + "total_quantity": { "type": "integer" }, - "asset_tag": { - "type": "string" + "damage_rate_percent": { + "type": "number", + "format": "double", + "nullable": true }, - "qr": { - "$ref": "#/components/schemas/QrCode" + "loss_rate_percent": { + "type": "number", + "format": "double", + "nullable": true } }, "required": [ - "asset_tag", - "id", - "qr" + "damage_rate_percent", + "damaged_quantity", + "loss_rate_percent", + "lost_quantity", + "product_id", + "product_name", + "total_quantity" ] }, - "AssetGenerateResult": { + "DamagedMissingReport": { "type": "object", "properties": { - "assets": { + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/AssetGenerateItem" + "type": "array", + "items": {} } }, - "print_batch_id": { - "type": "integer", - "nullable": true - } - }, - "required": [ - "assets", - "print_batch_id" - ] - }, - "AssetQrHistory": { - "type": "object", - "properties": { - "asset": { - "type": "integer" - }, - "scans": { + "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/QrHistoryItem" + "$ref": "#/components/schemas/DamagedMissingReportRow" } } }, "required": [ - "asset", - "scans" - ] - }, - "AssignBox": { - "type": "object", - "properties": { - "box_code": { - "type": "string" - } - }, - "required": [ - "box_code" - ] - }, - "AssignOperator": { - "type": "object", - "properties": { - "user_id": { - "type": "integer" - }, - "access_level": { - "type": "string", - "maxLength": 16 - } - }, - "required": [ - "access_level", - "user_id" + "rows", + "typed_rows" ] }, - "AttendanceCorrectionResponse": { + "DamagedMissingReportRow": { "type": "object", "properties": { - "registration_id": { + "makerspace_id": { "type": "integer" }, - "status": { + "product": { "type": "string" }, - "revoked_certificates": { - "type": "integer" - } - }, - "required": [ - "registration_id", - "revoked_certificates", - "status" - ] - }, - "AttendedEvent": { - "type": "object", - "properties": { - "id": { + "damaged_quantity": { "type": "integer" }, - "title": { - "type": "string" - }, - "starts_at": { - "type": "string", - "format": "date-time" + "missing_quantity": { + "type": "integer" } }, "required": [ - "id", - "starts_at", - "title" + "damaged_quantity", + "missing_quantity", + "product" ] }, - "AudienceBe5Enum": { - "enum": [ - "member", - "public" - ], - "type": "string", - "description": "* `member` - Active members\n* `public` - All accounts" - }, - "AuditLog": { + "Dashboard": { "type": "object", "properties": { - "id": { + "upcoming_events": { "type": "integer", - "readOnly": true + "default": 0 }, - "actor": { + "pending_event_registrations": { "type": "integer", - "readOnly": true, - "nullable": true - }, - "action": { - "type": "string", - "readOnly": true + "default": 0 }, - "makerspace": { + "pending_bookings": { "type": "integer", - "readOnly": true, - "nullable": true - }, - "target_type": { - "type": "string", - "readOnly": true - }, - "target_id": { - "type": "string", - "readOnly": true - }, - "meta": { - "type": "object", - "additionalProperties": {}, - "readOnly": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "action_label": { - "type": "string", - "readOnly": true - }, - "actor_display": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "meta_redacted": { - "type": "boolean", - "readOnly": true - }, - "target_link": { - "allOf": [ - { - "$ref": "#/components/schemas/AuditTargetLink" - } - ], - "nullable": true, - "readOnly": true - } - }, - "required": [ - "action", - "action_label", - "actor", - "actor_display", - "created_at", - "id", - "makerspace", - "meta", - "meta_redacted", - "target_id", - "target_link", - "target_type" - ] - }, - "AuditTargetLink": { - "type": "object", - "properties": { - "label": { - "type": "string" - }, - "path": { - "type": "string" - } - }, - "required": [ - "label", - "path" - ] - }, - "AuditVerificationBadRequest": { - "oneOf": [ - { - "$ref": "#/components/schemas/AuditVerificationError" - }, - { - "$ref": "#/components/schemas/AuditVerificationFilterError" - } - ] - }, - "AuditVerificationDetailError": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - }, - "required": [ - "detail" - ] - }, - "AuditVerificationError": { - "type": "object", - "properties": { - "code": { - "type": "string" - }, - "detail": { - "type": "string" - } - }, - "required": [ - "code", - "detail" - ] - }, - "AuditVerificationFilterError": { - "type": "object", - "properties": { - "action": { - "type": "array", - "items": { - "type": "string" - } - }, - "target_type": { - "type": "array", - "items": { - "type": "string" - } - }, - "target_id": { - "type": "array", - "items": { - "type": "string" - } + "default": 0 }, - "actor": { - "type": "array", - "items": { - "type": "string" - } + "upcoming_bookings": { + "type": "integer", + "default": 0 }, - "start": { - "type": "array", - "items": { - "type": "string" - } + "scope_mode": { + "$ref": "#/components/schemas/ScopeModeEnum" }, - "end": { + "widgets": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/DashboardWidget" } }, - "non_field_errors": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "AuthMembership": { - "type": "object", - "properties": { - "id": { - "type": "integer" + "queue_tiles": { + "$ref": "#/components/schemas/DashboardQueue" }, - "slug": { - "type": "string" + "overdue_loans": { + "type": "integer", + "default": 0 }, - "role": { - "type": "string", - "nullable": true + "pending_requests": { + "type": "integer", + "default": 0 }, - "role_id": { + "awaiting_issue": { "type": "integer", - "nullable": true + "default": 0 }, - "role_name": { - "type": "string" + "open_problem_reports": { + "type": "integer", + "default": 0 }, - "role_slug": { - "type": "string", - "nullable": true + "low_stock": { + "type": "integer", + "default": 0 }, - "source": { - "$ref": "#/components/schemas/AuthMembershipSourceEnum" + "pending_prints": { + "type": "integer", + "default": 0 }, - "actions": { - "type": "array", - "items": { - "type": "string" - } + "active_prints": { + "type": "integer", + "default": 0 }, - "can_configure_machine_types": { - "type": "boolean" + "prints_awaiting_collection": { + "type": "integer", + "default": 0 }, - "is_machine_only": { - "type": "boolean" + "failed_emails": { + "type": "integer", + "default": 0 }, - "can_refer": { - "type": "boolean" + "stocktakes_awaiting_approval": { + "type": "integer", + "default": 0 }, - "can_verify": { - "type": "boolean" + "warranty_expiring": { + "type": "integer", + "default": 0 }, - "verified_at": { - "type": "string", - "format": "date-time", - "nullable": true + "maintenance_overdue": { + "type": "integer", + "default": 0 }, - "referrals_enabled": { - "type": "boolean" + "pending_payments": { + "type": "integer", + "default": 0 + }, + "outstanding_by_currency": { + "type": "object", + "additionalProperties": { + "type": "string" + } } }, "required": [ - "actions", - "can_configure_machine_types", - "can_refer", - "can_verify", - "id", - "is_machine_only", - "referrals_enabled", - "role", - "role_id", - "role_name", - "role_slug", - "slug", - "source", - "verified_at" + "scope_mode" ] }, - "AuthMembershipSourceEnum": { - "enum": [ - "membership", - "organization" - ], - "type": "string", - "description": "* `membership` - membership\n* `organization` - organization" - }, - "AuthUserPayload": { + "DashboardQueue": { "type": "object", "properties": { - "id": { + "ready_to_issue": { "type": "integer" }, - "username": { - "type": "string" - }, - "email": { - "type": "string", - "format": "email" + "due_returns": { + "type": "integer" }, - "display_name": { + "pickups": { + "type": "integer" + } + } + }, + "DashboardWidget": { + "type": "object", + "properties": { + "key": { "type": "string" }, - "phone": { + "title": { "type": "string" }, - "email_verified": { - "type": "boolean" - }, - "role": { + "required_action": { "type": "string" }, - "is_superuser": { - "type": "boolean" + "required_modules": { + "type": "array", + "items": { + "type": "string" + } }, - "must_change_password": { - "type": "boolean" + "kind": { + "$ref": "#/components/schemas/DashboardWidgetKindEnum" }, - "makerspaces": { + "report_keys": { "type": "array", "items": { - "$ref": "#/components/schemas/AuthMembership" + "type": "string" } - } - }, - "required": [ - "display_name", - "email", - "email_verified", - "id", - "is_superuser", - "makerspaces", - "must_change_password", - "phone", - "role", - "username" - ] - }, - "AvailabilityEnum": { - "type": "string", - "enum": [ - "Available", - "Limited", - "Full" - ] - }, - "BackupArchive": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "scope": { - "allOf": [ - { - "$ref": "#/components/schemas/ScopeEnum" - } - ], - "readOnly": true }, - "makerspace": { + "default_period": { "type": "integer", - "readOnly": true, - "nullable": true + "description": "Inclusive local calendar days." }, - "status": { + "data": { + "type": "object", + "additionalProperties": {} + }, + "chart_spec": { "allOf": [ { - "$ref": "#/components/schemas/BackupArchiveStatusEnum" + "$ref": "#/components/schemas/ReportChartSpec" } ], - "readOnly": true - }, - "manifest": { - "readOnly": true - }, - "size_bytes": { - "type": "integer", - "readOnly": true - }, - "age_encrypted": { - "type": "boolean", - "readOnly": true - }, - "failure_detail": { - "type": "string", - "readOnly": true - }, - "started_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "completed_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "expires_at": { - "type": "string", - "format": "date-time", - "readOnly": true, "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "purge_warning": { - "type": "string", - "readOnly": true } }, "required": [ - "age_encrypted", - "completed_at", - "created_at", - "expires_at", - "failure_detail", - "id", - "makerspace", - "manifest", - "purge_warning", - "scope", - "size_bytes", - "started_at", - "status" + "chart_spec", + "data", + "default_period", + "key", + "kind", + "report_keys", + "required_action", + "required_modules", + "title" ] }, - "BackupArchiveStatusEnum": { + "DashboardWidgetKindEnum": { "enum": [ - "pending", - "running", - "promoting", - "available", - "failed", - "expired" + "count", + "trend", + "top_n", + "heatmap", + "queue" ], "type": "string", - "description": "* `pending` - Pending\n* `running` - Running\n* `promoting` - Promoting\n* `available` - Available\n* `failed` - Failed\n* `expired` - Expired" + "description": "* `count` - count\n* `trend` - trend\n* `top_n` - top_n\n* `heatmap` - heatmap\n* `queue` - queue" }, - "BackupCustody": { + "DataExportCreate": { "type": "object", "properties": { - "superadmin_access_enabled": { - "type": "boolean" - }, - "verified_recipient_count": { - "type": "integer" - }, - "archive_custody": { + "fidelity": { "allOf": [ { - "$ref": "#/components/schemas/CustodyState" + "$ref": "#/components/schemas/FidelityEnum" } ], - "nullable": true + "default": "REDACTED" + } + } + }, + "DataExportDownloadUrl": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" }, - "tenant_exit_custody": { - "allOf": [ - { - "$ref": "#/components/schemas/CustodyState" - } - ], - "nullable": true + "expires_at": { + "type": "string", + "format": "date-time" } }, "required": [ - "archive_custody", - "superadmin_access_enabled", - "tenant_exit_custody", - "verified_recipient_count" + "expires_at", + "url" ] }, - "BackupDestination": { + "DataExportJob": { "type": "object", - "description": "Credentials are write-only; reads report only whether they are stored.", "properties": { "id": { - "type": "integer", + "type": "string", + "format": "uuid", "readOnly": true }, - "name": { + "fidelity": { "type": "string", - "maxLength": 64, - "pattern": "^[-a-zA-Z0-9_]+$" + "readOnly": true }, - "kind": { + "status": { "allOf": [ { - "$ref": "#/components/schemas/BackupDestinationKindEnum" + "$ref": "#/components/schemas/StatusE1dEnum" } ], "readOnly": true }, - "endpoint_url": { + "manifest": { + "readOnly": true + }, + "failure_code": { + "readOnly": true, "oneOf": [ { - "type": "string", - "format": "uri", - "maxLength": 300 + "$ref": "#/components/schemas/FailureCodeEnum" }, { - "type": "string", - "maxLength": 0 + "$ref": "#/components/schemas/BlankEnum" } ] }, - "bucket": { - "type": "string", - "maxLength": 255 - }, - "region": { - "type": "string", - "maxLength": 64 - }, - "prefix": { - "type": "string", - "maxLength": 200 - }, - "addressing_style": { - "$ref": "#/components/schemas/AddressingStyleEnum" - }, - "access_key_id": { - "type": "string", - "writeOnly": true, - "maxLength": 200 - }, - "secret_access_key": { + "failure_detail": { "type": "string", - "writeOnly": true, - "maxLength": 200 - }, - "credentials_set": { - "type": "boolean", "readOnly": true }, - "enabled": { - "type": "boolean" - }, - "retention_days": { - "type": "integer", - "maximum": 3650, - "minimum": 1 - }, - "created_at": { + "deadline_at": { "type": "string", "format": "date-time", - "readOnly": true + "readOnly": true, + "nullable": true }, - "updated_at": { + "snapshot_at": { "type": "string", "format": "date-time", - "readOnly": true + "readOnly": true, + "nullable": true }, - "retired_at": { + "started_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "last_success_at": { + "completed_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "last_error_code": { + "expires_at": { "type": "string", + "format": "date-time", "readOnly": true - } - }, - "required": [ - "bucket", - "created_at", - "credentials_set", - "id", - "kind", - "last_error_code", - "last_success_at", - "name", - "retired_at", - "updated_at" - ] - }, - "BackupDestinationKindEnum": { - "enum": [ - "s3" - ], - "type": "string", - "description": "* `s3` - S3-compatible (AWS, R2, Supabase Storage, MinIO)" - }, - "BackupDestinationPage": { - "type": "object", - "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "format": "uri", - "nullable": true }, - "previous": { + "created_at": { "type": "string", - "format": "uri", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BackupDestination" - } + "format": "date-time", + "readOnly": true } }, "required": [ - "count", - "next", - "previous", - "results" + "completed_at", + "created_at", + "deadline_at", + "expires_at", + "failure_code", + "failure_detail", + "fidelity", + "id", + "manifest", + "snapshot_at", + "started_at", + "status" ] }, - "BackupDestinationUpload": { + "Delivery": { "type": "object", "properties": { "id": { - "type": "integer", - "readOnly": true - }, - "destination": { - "type": "string", - "readOnly": true - }, - "archive_id_snapshot": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "scope_snapshot": { - "type": "string", - "readOnly": true - }, - "makerspace_id_snapshot": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "archive_completed_at_snapshot": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "expected_size_bytes": { - "type": "integer", - "readOnly": true - }, - "expected_sha256": { - "type": "string", - "readOnly": true + "type": "integer" }, - "integrity_source": { - "allOf": [ - { - "$ref": "#/components/schemas/IntegritySourceEnum" - } - ], - "readOnly": true + "kind": { + "$ref": "#/components/schemas/DeliveryKindEnum" }, - "remote_locator": { - "type": "string", - "readOnly": true + "channel": { + "type": "string" }, - "state": { - "allOf": [ - { - "$ref": "#/components/schemas/BackupDestinationUploadStateEnum" - } - ], - "readOnly": true + "feature": { + "type": "string" }, - "attempts": { + "event": { + "type": "string" + }, + "destination_id": { "type": "integer", - "readOnly": true + "nullable": true }, - "next_attempt_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "destination_label": { + "type": "string" }, - "uploaded_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "status": { + "type": "string" }, - "verified_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "error": { + "type": "string" }, - "expires_at": { + "attempts": { + "type": "integer" + }, + "retry_generation": { + "type": "integer" + }, + "created_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" }, - "deleted_at": { + "sent_at": { "type": "string", "format": "date-time", - "readOnly": true, "nullable": true - }, - "last_error_code": { - "type": "string", - "readOnly": true } }, "required": [ - "archive_completed_at_snapshot", - "archive_id_snapshot", "attempts", - "deleted_at", - "destination", - "expected_sha256", - "expected_size_bytes", - "expires_at", + "channel", + "created_at", + "destination_id", + "destination_label", + "error", + "event", + "feature", "id", - "integrity_source", - "last_error_code", - "makerspace_id_snapshot", - "next_attempt_at", - "remote_locator", - "scope_snapshot", - "state", - "uploaded_at", - "verified_at" + "kind", + "retry_generation", + "sent_at", + "status" ] }, - "BackupDestinationUploadPage": { + "DeliveryEnum": { + "enum": [ + "web", + "device" + ], + "type": "string", + "description": "* `web` - Web\n* `device` - Device" + }, + "DeliveryKindEnum": { + "enum": [ + "email", + "channel" + ], + "type": "string", + "description": "* `email` - email\n* `channel` - channel" + }, + "DeliveryPage": { "type": "object", "properties": { "count": { @@ -70141,7 +91518,7 @@ "results": { "type": "array", "items": { - "$ref": "#/components/schemas/BackupDestinationUpload" + "$ref": "#/components/schemas/Delivery" } } }, @@ -70152,741 +91529,877 @@ "results" ] }, - "BackupDestinationUploadStateEnum": { - "enum": [ - "pending", - "uploading", - "bytes_verified", - "abandoned", - "deleted" - ], - "type": "string", - "description": "* `pending` - Pending upload\n* `uploading` - Uploading\n* `bytes_verified` - Bytes verified\n* `abandoned` - Abandoned (source no longer available)\n* `deleted` - Deleted by retention" + "DeliveryTest": { + "type": "object", + "properties": { + "destination_id": { + "type": "integer", + "minimum": 1 + } + } }, - "BackupDownload": { + "DeliveryTestResult": { "type": "object", "properties": { - "url": { - "type": "string", - "format": "uri" + "kind": { + "type": "string" }, - "expires_at": { - "type": "string", - "format": "date-time" + "id": { + "type": "integer" }, - "purge_warning": { + "status": { "type": "string" } }, "required": [ - "expires_at", - "purge_warning", - "url" + "id", + "kind", + "status" ] }, - "BadgePdfRequest": { + "DeploymentIdentity": { "type": "object", "properties": { - "registration_ids": { - "type": "array", - "items": { - "type": "integer", - "minimum": 1 - }, - "maxItems": 200 + "algorithm": { + "type": "string" }, - "template_override": { - "allOf": [ - { - "$ref": "#/components/schemas/BadgeTemplate" - } - ], - "nullable": true + "deployment_id": { + "type": "string" }, - "include_attended": { - "type": "boolean", - "default": false + "public_key": { + "type": "string" + }, + "fingerprint": { + "type": "string" + }, + "age_recipient": { + "type": "string" } }, "required": [ - "registration_ids" + "age_recipient", + "algorithm", + "deployment_id", + "fingerprint", + "public_key" ] }, - "BadgeTemplate": { + "DestinationDispatch": { "type": "object", "properties": { - "version": { - "type": "integer" - }, - "paper_size": { - "$ref": "#/components/schemas/PaperSizeEnum" - }, - "orientation": { - "$ref": "#/components/schemas/OrientationEnum" - }, - "page_width_mm": { - "type": "number", - "format": "double", - "nullable": true - }, - "page_height_mm": { - "type": "number", - "format": "double", - "nullable": true - }, - "card_width_mm": { - "type": "number", - "format": "double" - }, - "card_height_mm": { - "type": "number", - "format": "double" - }, - "margin_mm": { - "type": "number", - "format": "double" - }, - "gap_mm": { - "type": "number", - "format": "double" - }, - "template": { + "detail": { "type": "string" - }, - "fields": { - "type": "array", - "items": { - "type": "string", - "maxLength": 80 - } - }, - "font_size_pt": { - "type": "number", - "format": "double" - }, - "name_font_size_pt": { - "type": "number", - "format": "double" - }, - "text_align": { - "$ref": "#/components/schemas/TextAlignEnum" - }, - "include_qr": { - "type": "boolean" } - } + }, + "required": [ + "detail" + ] }, - "BillingPolicyError": { - "oneOf": [ - { - "$ref": "#/components/schemas/HardwareRequestError" + "DestinationProbe": { + "type": "object", + "properties": { + "ok": { + "type": "boolean" }, - { - "$ref": "#/components/schemas/BillingPolicyValidationError" + "error_code": { + "type": "string" } + }, + "required": [ + "error_code", + "ok" ] }, - "BillingPolicyValidationError": { + "DestinationScope": { "type": "object", "properties": { - "number_prefix": { - "type": "array", - "items": { - "type": "string" - } - }, - "legal_name": { - "type": "array", - "items": { - "type": "string" - } - }, - "address": { - "type": "array", - "items": { - "type": "string" - } - }, - "tax_id": { - "type": "array", - "items": { - "type": "string" - } - }, - "footer": { - "type": "array", - "items": { - "type": "string" - } - }, - "account_codes": {}, - "payment_terms_days": { + "machine_type_ids": { "type": "array", "items": { - "type": "string" + "type": "integer" } }, - "reminder_days_before": {}, - "reminder_days_after": {}, - "max_reminders": { + "machine_ids": { "type": "array", "items": { - "type": "string" + "type": "integer" } }, - "non_field_errors": { + "category_ids": { "type": "array", "items": { - "type": "string" + "type": "integer" } } } }, - "BillingSettings": { + "Device": { "type": "object", "properties": { - "payment_terms_days": { + "id": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "makerspace": { "type": "integer", - "maximum": 365, - "minimum": 0 + "readOnly": true }, - "reminder_days_before": { - "type": "array", - "items": { - "type": "integer", - "maximum": 365, - "minimum": 1 - }, - "maxItems": 20 + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/KindE45Enum" + } + ], + "readOnly": true }, - "reminder_days_after": { - "type": "array", - "items": { - "type": "integer", - "maximum": 365, - "minimum": 1 - }, - "maxItems": 20 + "name": { + "type": "string", + "readOnly": true }, - "max_reminders": { + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/DeviceStatusEnum" + } + ], + "readOnly": true + }, + "public_key": { + "type": "string", + "readOnly": true, + "nullable": true + }, + "key_fingerprint": { + "type": "string", + "readOnly": true, + "nullable": true + }, + "key_version": { "type": "integer", - "maximum": 20, - "minimum": 0 + "readOnly": true }, - "number_prefix": { + "last_seen_at": { "type": "string", - "maxLength": 32 + "format": "date-time", + "readOnly": true, + "nullable": true }, - "legal_name": { + "offline_threshold_seconds": { + "type": "integer", + "readOnly": true + }, + "offline_since": { "type": "string", - "maxLength": 150 + "format": "date-time", + "readOnly": true, + "nullable": true }, - "address": { + "offline_notified_at": { "type": "string", - "maxLength": 1000 + "format": "date-time", + "readOnly": true, + "nullable": true }, - "tax_id": { + "created_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { "type": "string", - "maxLength": 64 + "format": "date-time", + "readOnly": true }, - "footer": { + "revoked_at": { "type": "string", - "maxLength": 1000 + "format": "date-time", + "readOnly": true, + "nullable": true }, - "account_codes": {} + "revoked_by": { + "type": "integer", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "created_at", + "created_by", + "id", + "key_fingerprint", + "key_version", + "kind", + "last_seen_at", + "makerspace", + "name", + "offline_notified_at", + "offline_since", + "offline_threshold_seconds", + "public_key", + "revoked_at", + "revoked_by", + "status" + ] + }, + "DeviceAttestationPayload": { + "anyOf": [ + { + "type": "object", + "required": [ + "type", + "token" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/DeviceAttestationPayloadTypeEnum" + }, + "token": { + "type": "string", + "maxLength": 65536 + } + } + }, + { + "type": "object", + "required": [ + "type", + "key_id", + "attestation" + ], + "properties": { + "type": { + "$ref": "#/components/schemas/DeviceAttestationPayloadTypeEnum" + }, + "key_id": { + "type": "string", + "format": "byte", + "maxLength": 64 + }, + "attestation": { + "type": "string", + "format": "byte", + "maxLength": 65536 + } + } + }, + { + "type": "object", + "required": [ + "type" + ], + "additionalProperties": false, + "properties": { + "type": { + "$ref": "#/components/schemas/DeviceAttestationPayloadTypeEnum" + } + }, + "description": "Only with development-insecure mode and a development registration." + }, + { + "type": "object", + "description": "Deployment-specific external verifier proof." + } + ] + }, + "DeviceAttestationPayloadTypeEnum": { + "type": "string", + "enum": [ + "play_integrity" + ] + }, + "DeviceChallengeResponse": { + "type": "object", + "properties": { + "challenge": { + "type": "string" + }, + "expires_in": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "challenge", + "expires_in" + ] + }, + "DeviceCreate": { + "type": "object", + "properties": { + "kind": { + "$ref": "#/components/schemas/KindE45Enum" + }, + "name": { + "type": "string", + "maxLength": 120 + }, + "offline_threshold_seconds": { + "type": "integer", + "maximum": 604800, + "minimum": 1, + "default": 300 + } + }, + "required": [ + "kind", + "name" + ] + }, + "DeviceCreated": { + "type": "object", + "properties": { + "device": { + "$ref": "#/components/schemas/Device" + }, + "enrollment": { + "$ref": "#/components/schemas/EnrollmentGrant" + } }, "required": [ - "max_reminders", - "reminder_days_after", - "reminder_days_before" + "device", + "enrollment" ] }, - "Binding": { + "DeviceError": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "DeviceGrant": { "type": "object", "properties": { "id": { "type": "string", - "format": "uuid", - "readOnly": true + "format": "uuid" }, - "makerspace": { - "type": "integer", - "readOnly": true + "platform": { + "type": "string" }, - "device": { - "type": "string", - "format": "uuid", - "readOnly": true + "app_id": { + "type": "string" }, - "machine": { - "type": "integer", - "readOnly": true, - "nullable": true + "environment": { + "type": "string" }, - "enabled": { - "type": "boolean", - "readOnly": true + "status": { + "type": "string" }, - "auto_apply_terminal": { - "type": "boolean", - "readOnly": true + "attested_at": { + "type": "string", + "format": "date-time" }, - "created_at": { + "last_used_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" }, - "updated_at": { + "created_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" } }, "required": [ - "auto_apply_terminal", + "app_id", + "attested_at", "created_at", - "device", - "enabled", + "environment", "id", - "machine", - "makerspace", - "updated_at" + "last_used_at", + "platform", + "status" ] }, - "BindingCreate": { + "DeviceIdentity": { "type": "object", "properties": { - "device_id": { - "type": "string", - "format": "uuid" - }, - "machine_id": { - "type": "integer", - "minimum": 1 + "platform": { + "$ref": "#/components/schemas/PlatformEnum" }, - "enabled": { - "type": "boolean", - "default": true + "app_id": { + "type": "string", + "maxLength": 255, + "pattern": "^[A-Za-z0-9._-]{3,255}$" }, - "auto_apply_terminal": { - "type": "boolean", - "default": false + "environment": { + "$ref": "#/components/schemas/EnvironmentEnum" } }, "required": [ - "device_id", - "machine_id" - ] - }, - "BlankEnum": { - "enum": [ - "" + "app_id", + "environment", + "platform" ] }, - "BookableSpaceAdmin": { + "DeviceLogin": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "platform": { + "$ref": "#/components/schemas/PlatformEnum" }, - "public_token": { + "app_id": { "type": "string", - "format": "uuid", - "readOnly": true + "maxLength": 255, + "pattern": "^[A-Za-z0-9._-]{3,255}$" }, - "makerspace_id": { - "type": "integer", - "readOnly": true + "environment": { + "$ref": "#/components/schemas/EnvironmentEnum" }, - "name": { + "username": { "type": "string", - "readOnly": true + "maxLength": 254 }, - "kind": { - "allOf": [ - { - "$ref": "#/components/schemas/Kind3bfEnum" - } - ], - "readOnly": true + "password": { + "type": "string", + "writeOnly": true, + "maxLength": 1024 }, - "description": { + "challenge": { "type": "string", - "readOnly": true + "maxLength": 512 }, - "capacity": { - "type": "integer", - "readOnly": true + "attestation": { + "$ref": "#/components/schemas/DeviceAttestationPayload" + } + }, + "required": [ + "app_id", + "attestation", + "challenge", + "environment", + "password", + "platform", + "username" + ] + }, + "DeviceLoginResult": { + "oneOf": [ + { + "$ref": "#/components/schemas/DeviceTokenResponse" }, - "location": { + { + "$ref": "#/components/schemas/MFAChallengeResponse" + } + ] + }, + "DeviceLogoutResponse": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "DeviceRefresh": { + "type": "object", + "properties": { + "refresh": { "type": "string", - "readOnly": true + "writeOnly": true, + "maxLength": 4096 + } + }, + "required": [ + "refresh" + ] + }, + "DeviceRefreshResponse": { + "type": "object", + "properties": { + "access": { + "type": "string" }, - "image_url": { - "type": "string", - "readOnly": true + "refresh": { + "type": "string" }, - "is_public": { - "type": "boolean", - "readOnly": true + "device_grant": { + "$ref": "#/components/schemas/DeviceGrant" + } + }, + "required": [ + "access", + "device_grant", + "refresh" + ] + }, + "DeviceStatusEnum": { + "enum": [ + "pending", + "active", + "revoked" + ], + "type": "string", + "description": "* `pending` - Pending enrollment\n* `active` - Active\n* `revoked` - Revoked" + }, + "DeviceTokenResponse": { + "type": "object", + "properties": { + "access": { + "type": "string" }, - "show_public_availability": { - "type": "boolean", - "readOnly": true + "refresh": { + "type": "string" }, - "show_public_booker_names": { + "user": { + "type": "object", + "additionalProperties": {} + }, + "device_grant": { + "$ref": "#/components/schemas/DeviceGrant" + } + }, + "required": [ + "access", + "device_grant", + "refresh", + "user" + ] + }, + "DirectLoan": { + "type": "object", + "properties": { + "has_kit_checklist": { "type": "boolean", "readOnly": true }, - "approval_mode": { - "allOf": [ - { - "$ref": "#/components/schemas/ApprovalModeE35Enum" - } - ], + "public_token": { + "type": "string", + "format": "uuid", "readOnly": true }, - "min_booking_duration_minutes": { - "type": "integer", + "status": { + "type": "string", "readOnly": true }, - "max_booking_duration_minutes": { - "type": "integer", + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicToolLoanItem" + }, "readOnly": true }, - "booking_lead_time_minutes": { + "id": { "type": "integer", "readOnly": true }, - "max_booking_advance_days": { - "type": "integer", + "target_type": { + "type": "string", "readOnly": true }, - "buffer_before_minutes": { - "type": "integer", + "target_label": { + "type": "string", "readOnly": true }, - "buffer_after_minutes": { + "container_id": { "type": "integer", "readOnly": true }, - "cancellation_cutoff_hours": { - "type": "integer", + "container_label": { + "type": "string", + "nullable": true, "readOnly": true }, - "custom_form": { + "due_at": { + "type": "string", + "format": "date-time", "readOnly": true, "nullable": true }, - "requester_notifications_enabled": { - "type": "boolean", + "issue_evidence_id": { + "type": "integer", "readOnly": true, "nullable": true }, - "payment_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true - }, - "machine_type": { + "return_evidence_id": { "type": "integer", "readOnly": true, "nullable": true }, - "effective_requester_notifications_enabled": { - "type": "boolean", + "return_notes": { + "type": "string", "readOnly": true }, - "is_active": { + "return_scan_required": { "type": "boolean", "readOnly": true }, - "created_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "created_at": { + "source": { "type": "string", - "format": "date-time", "readOnly": true }, - "updated_at": { - "type": "string", - "format": "date-time", + "issued_by": { + "allOf": [ + { + "$ref": "#/components/schemas/DirectLoanUserAttribution" + } + ], + "nullable": true, + "readOnly": true + }, + "return_items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DirectLoanReturnItem" + }, "readOnly": true } }, "required": [ - "approval_mode", - "booking_lead_time_minutes", - "buffer_after_minutes", - "buffer_before_minutes", - "cancellation_cutoff_hours", - "capacity", - "created_at", - "created_by_id", - "custom_form", - "description", - "effective_requester_notifications_enabled", + "container_id", + "container_label", + "due_at", + "has_kit_checklist", "id", - "image_url", - "is_active", - "is_public", - "kind", - "location", - "machine_type", - "makerspace_id", - "max_booking_advance_days", - "max_booking_duration_minutes", - "min_booking_duration_minutes", - "name", - "payment_amount", + "issue_evidence_id", + "issued_by", + "items", "public_token", - "requester_notifications_enabled", - "show_public_availability", - "show_public_booker_names", - "updated_at" + "return_evidence_id", + "return_items", + "return_notes", + "return_scan_required", + "source", + "status", + "target_label", + "target_type" ] }, - "BookableSpaceBookingRules": { + "DirectLoanIssue": { "type": "object", "properties": { - "min_booking_duration_minutes": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1 - }, - "max_booking_duration_minutes": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1 - }, - "booking_lead_time_minutes": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "max_booking_advance_days": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1 - }, - "buffer_before_minutes": { - "type": "integer", - "maximum": 1440, - "minimum": 0 - }, - "buffer_after_minutes": { - "type": "integer", - "maximum": 1440, - "minimum": 0 + "kit_checklist": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitCount" + }, + "maxItems": 3550 }, - "cancellation_cutoff_hours": { - "type": "integer", - "maximum": 8760, - "minimum": 0 + "borrower_id": { + "type": "integer" }, - "approval_mode": { - "$ref": "#/components/schemas/ApprovalModeE35Enum" - } - } - }, - "BookableSpaceListResponse": { - "type": "object", - "properties": { - "count": { + "evidence_id": { "type": "integer" }, - "next": { - "type": "string", - "nullable": true + "remark": { + "type": "string" }, - "previous": { - "type": "string", + "container_id": { + "type": "integer", "nullable": true }, - "results": { + "qr_payloads": { "type": "array", "items": { - "$ref": "#/components/schemas/BookableSpaceAdmin" - } - } - }, - "required": [ - "count", - "results" - ] - }, - "BookableSpaceWrite": { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 200 - }, - "kind": { - "allOf": [ - { - "$ref": "#/components/schemas/Kind3bfEnum" - } - ], - "default": "other" - }, - "description": { - "type": "string", - "default": "" - }, - "capacity": { - "type": "integer", - "minimum": 0, - "default": 0 - }, - "location": { - "type": "string", - "default": "", - "maxLength": 255 - }, - "is_public": { - "type": "boolean", - "default": false - }, - "show_public_availability": { - "type": "boolean", - "default": false - }, - "show_public_booker_names": { - "type": "boolean", - "default": false - }, - "custom_form": { - "nullable": true - }, - "requester_notifications_enabled": { - "type": "boolean", - "nullable": true + "type": "string", + "maxLength": 64 + }, + "maxItems": 50 }, - "payment_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "default": "0.00" + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DirectLoanItem" + } + } + }, + "required": [ + "borrower_id", + "evidence_id" + ] + }, + "DirectLoanItem": { + "type": "object", + "properties": { + "product_id": { + "type": "integer" }, - "machine_type": { + "quantity": { "type": "integer", - "minimum": 1, - "nullable": true + "maximum": 2147483647, + "minimum": 1 } }, "required": [ - "name" + "product_id", + "quantity" ] }, - "BookingAdmin": { + "DirectLoanMember": { "type": "object", "properties": { - "id": { + "membership_id": { "type": "integer", "readOnly": true }, - "public_token": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "space_id": { + "user_id": { "type": "integer", "readOnly": true }, - "name": { + "display_name": { "type": "string", "readOnly": true }, - "email": { + "username": { "type": "string", "readOnly": true }, - "phone": { - "type": "string", + "is_walk_in": { + "type": "boolean", "readOnly": true + } + }, + "required": [ + "display_name", + "is_walk_in", + "membership_id", + "user_id", + "username" + ] + }, + "DirectLoanReturn": { + "type": "object", + "properties": { + "kit_checklist": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitCount" + }, + "maxItems": 3550 }, - "starts_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "evidence_id": { + "type": "integer" }, - "ends_at": { + "notes": { + "type": "string" + }, + "qr_payload": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 64 }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/Status2caEnum" - } - ], - "readOnly": true + "resolutions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReturnItemResolution" + } + } + }, + "required": [ + "evidence_id", + "notes" + ] + }, + "DirectLoanReturnAsset": { + "type": "object", + "properties": { + "asset_id": { + "type": "integer" }, - "note": { - "type": "string", - "readOnly": true + "asset_tag": { + "type": "string" + } + }, + "required": [ + "asset_id", + "asset_tag" + ] + }, + "DirectLoanReturnItem": { + "type": "object", + "properties": { + "item_id": { + "type": "integer" }, - "custom_answers": { - "readOnly": true, - "nullable": true + "product_name": { + "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "remaining_quantity": { + "type": "integer" }, - "payment": { - "allOf": [ - { - "$ref": "#/components/schemas/StaffPaymentSummary" - } - ], - "nullable": true, - "readOnly": true + "tracking_mode": { + "type": "string" + }, + "assets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DirectLoanReturnAsset" + } } }, "required": [ - "created_at", - "custom_answers", - "email", - "ends_at", - "id", - "name", - "note", - "payment", - "phone", - "public_token", - "space_id", - "starts_at", - "status" + "assets", + "item_id", + "product_name", + "remaining_quantity", + "tracking_mode" ] }, - "BookingEligibleMember": { + "DirectLoanUserAttribution": { "type": "object", "properties": { - "member_id": { - "type": "integer", - "readOnly": true + "username": { + "type": "string" + }, + "role": { + "type": "string" + } + }, + "required": [ + "role", + "username" + ] + }, + "Directory": { + "type": "object", + "properties": { + "members": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DirectoryEntry" + } + }, + "hidden_count": { + "type": "integer" + } + }, + "required": [ + "hidden_count", + "members" + ] + }, + "DirectoryEntry": { + "type": "object", + "description": "The listing row. Display name and avatar only -- never email, never phone.\n\nOtherwise \"see who else is here\" is an address harvest performed by anyone a space\nadmits, which is a different thing from what the member agreed to publish.", + "properties": { + "membership_id": { + "type": "integer" }, "display_name": { + "type": "string" + }, + "headline": { + "type": "string" + }, + "avatar_url": { "type": "string", - "readOnly": true + "nullable": true } }, "required": [ + "avatar_url", "display_name", - "member_id" + "headline", + "membership_id" ] }, - "BookingEligibleMemberList": { + "Discovery": { "type": "object", "properties": { "count": { @@ -70894,16 +92407,18 @@ }, "next": { "type": "string", + "format": "uri", "nullable": true }, "previous": { "type": "string", + "format": "uri", "nullable": true }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/BookingEligibleMember" + "$ref": "#/components/schemas/DiscoveryCard" } } }, @@ -70914,50 +92429,100 @@ "results" ] }, - "BookingListResponse": { + "DiscoveryCard": { "type": "object", "properties": { - "count": { - "type": "integer" + "title": { + "type": "string" }, - "next": { + "description": { + "type": "string" + }, + "url": { "type": "string", - "nullable": true + "format": "uri" }, - "previous": { + "image_url": { "type": "string", + "format": "uri", "nullable": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BookingAdmin" - } + "date": { + "type": "string" + }, + "location": { + "type": "string" + }, + "availability": { + "type": "string" + }, + "audience_label": { + "type": "string" + }, + "revision": { + "type": "string" + }, + "registration_count": { + "type": "integer", + "nullable": true + }, + "makerspace_name": { + "type": "string" } }, "required": [ - "count", - "results" + "audience_label", + "availability", + "date", + "description", + "image_url", + "location", + "makerspace_name", + "registration_count", + "revision", + "title", + "url" ] }, - "BookingMachineType": { + "DispositionEnum": { + "enum": [ + "needs_fix", + "remove" + ], + "type": "string", + "description": "* `needs_fix` - needs_fix\n* `remove` - remove" + }, + "DocTypeEnum": { + "enum": [ + "manual", + "sop" + ], + "type": "string", + "description": "* `manual` - Manual\n* `sop` - SOP" + }, + "DocumentFinalize": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "object_key": { + "type": "string", + "maxLength": 255 }, - "name": { + "doc_type": { "type": "string", - "readOnly": true + "maxLength": 16 + }, + "original_filename": { + "type": "string", + "maxLength": 255 } }, "required": [ - "id", - "name" + "doc_type", + "object_key", + "original_filename" ] }, - "BookingMachineTypePage": { + "DocumentPage": { "type": "object", "properties": { "count": { @@ -70976,7 +92541,7 @@ "results": { "type": "array", "items": { - "$ref": "#/components/schemas/BookingMachineType" + "$ref": "#/components/schemas/PaymentDocument" } } }, @@ -70987,513 +92552,766 @@ "results" ] }, - "BookingReschedule": { + "DocumentPresign": { "type": "object", "properties": { - "starts_at": { + "filename": { "type": "string", - "format": "date-time" + "maxLength": 255 }, - "ends_at": { + "content_type": { "type": "string", - "format": "date-time" + "maxLength": 100 } }, "required": [ - "ends_at", - "starts_at" + "content_type", + "filename" ] }, - "BookingUtilizationReport": { + "DomainVerificationRecord": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "host": { + "type": "string" }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BookingUtilizationRow" - } + "type": { + "type": "string" + }, + "value": { + "type": "string" } }, "required": [ - "rows", - "typed_rows" + "host", + "type", + "value" ] }, - "BookingUtilizationRow": { + "DomainVerificationResponse": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, - "space_id": { - "type": "integer" - }, - "space_name": { - "type": "string" + "status": { + "$ref": "#/components/schemas/StatusA39Enum" }, - "kind": { + "token": { "type": "string" }, - "is_active": { - "type": "boolean" - }, - "booked": { - "type": "integer" - }, - "completed": { - "type": "integer" + "expected_record": { + "allOf": [ + { + "$ref": "#/components/schemas/DomainVerificationRecord" + } + ], + "nullable": true }, - "no_show": { - "type": "integer" + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "cancelled": { - "type": "integer" + "detail": { + "type": "string" + } + }, + "required": [ + "detail", + "expected_record", + "status", + "token", + "verified_at" + ] + }, + "DownloadGrant": { + "type": "object", + "properties": { + "url": { + "type": "string" }, - "upcoming": { + "expires_in": { "type": "integer" - }, - "reserved_hours": { + } + }, + "required": [ + "expires_in", + "url" + ] + }, + "EducationEntry": { + "type": "object", + "properties": { + "institution": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "maxLength": 200 }, - "completed_hours": { + "qualification": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "default": "", + "maxLength": 200 }, - "window_hours": { + "year": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", - "nullable": true - }, - "reservation_utilization_percent": { - "type": "number", - "format": "double", - "nullable": true - }, - "no_show_rate_percent": { - "type": "number", - "format": "double", - "nullable": true + "default": "", + "maxLength": 20 } }, "required": [ - "booked", - "cancelled", - "completed", - "completed_hours", - "is_active", - "kind", - "no_show", - "no_show_rate_percent", - "reservation_utilization_percent", - "reserved_hours", - "space_id", - "space_name", - "upcoming", - "window_hours" + "institution" ] }, - "Bounce": { + "EmailLog": { "type": "object", "properties": { - "event_id": { + "id": { + "type": "integer", + "readOnly": true + }, + "to_email": { "type": "string", - "maxLength": 200 + "readOnly": true }, - "address": { + "subject": { "type": "string", - "format": "email", - "maxLength": 254 + "readOnly": true }, - "reason": { - "$ref": "#/components/schemas/BounceReasonEnum" + "stream": { + "type": "string", + "readOnly": true + }, + "event": { + "type": "string", + "readOnly": true + }, + "audience": { + "type": "string", + "readOnly": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/EmailLogStatusEnum" + } + ], + "readOnly": true + }, + "error": { + "type": "string", + "readOnly": true + }, + "attempts": { + "type": "integer", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "sent_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true } }, "required": [ - "address", - "event_id", - "reason" + "attempts", + "audience", + "created_at", + "error", + "event", + "id", + "sent_at", + "status", + "stream", + "subject", + "to_email" ] }, - "BounceReasonEnum": { + "EmailLogStatusEnum": { "enum": [ - "bounce", - "complaint" + "pending", + "sending", + "sent", + "failed", + "skipped" ], "type": "string", - "description": "* `bounce` - bounce\n* `complaint` - complaint" - }, - "BounceResult": { - "type": "object", - "properties": { - "accepted": { - "type": "boolean" - } - }, - "required": [ - "accepted" - ] + "description": "* `pending` - Pending\n* `sending` - Sending\n* `sent` - Sent\n* `failed` - Failed\n* `skipped` - Skipped" }, - "Box": { + "EmailTemplateDetail": { "type": "object", "properties": { - "id": { - "type": "integer", + "stream": { + "type": "string", "readOnly": true }, - "makerspace": { - "type": "integer", + "audience": { + "type": "string", "readOnly": true }, - "parent": { - "type": "integer", - "nullable": true - }, - "code": { + "key": { "type": "string", "readOnly": true }, "label": { "type": "string", - "maxLength": 200 - }, - "location": { - "type": "string", - "maxLength": 200 + "readOnly": true }, "description": { - "type": "string" + "type": "string", + "readOnly": true }, - "kit_components": { + "fields": { "type": "array", "items": { - "$ref": "#/components/schemas/KitComponent" + "type": "object", + "additionalProperties": {} }, - "maxItems": 50 + "readOnly": true + }, + "subject": { + "type": "string", + "readOnly": true + }, + "text_body": { + "type": "string", + "readOnly": true + }, + "html_body": { + "type": "string", + "readOnly": true }, "is_active": { - "type": "boolean" + "type": "boolean", + "readOnly": true }, - "qr_code_id": { - "type": "integer", - "nullable": true, + "is_overridden": { + "type": "boolean", "readOnly": true }, - "created_at": { + "default_subject": { "type": "string", - "format": "date-time", "readOnly": true }, - "updated_at": { + "default_text": { + "type": "string", + "readOnly": true + }, + "default_html": { "type": "string", - "format": "date-time", "readOnly": true } }, "required": [ - "code", - "created_at", - "id", + "audience", + "default_html", + "default_subject", + "default_text", + "description", + "fields", + "html_body", + "is_active", + "is_overridden", + "key", "label", - "makerspace", - "qr_code_id", - "updated_at" + "stream", + "subject", + "text_body" ] }, - "Broadcast": { + "EmailTemplateListItem": { "type": "object", "properties": { - "id": { - "type": "integer", + "stream": { + "type": "string", "readOnly": true }, - "title": { + "audience": { "type": "string", "readOnly": true }, - "body": { + "key": { "type": "string", "readOnly": true }, - "created_at": { + "label": { "type": "string", - "format": "date-time", + "readOnly": true + }, + "is_active": { + "type": "boolean", + "readOnly": true + }, + "is_overridden": { + "type": "boolean", + "readOnly": true + }, + "can_edit_space_default": { + "type": "boolean", + "readOnly": true + }, + "overridable_types": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineTypeOption" + }, "readOnly": true } }, "required": [ - "body", - "created_at", - "id", - "title" + "audience", + "can_edit_space_default", + "is_active", + "is_overridden", + "key", + "label", + "overridable_types", + "stream" ] }, - "BroadcastInput": { + "EmailTemplatePreviewRequest": { "type": "object", "properties": { - "title": { + "stream": { + "type": "string" + }, + "audience": { + "type": "string" + }, + "key": { + "type": "string" + }, + "machine_type_id": { + "type": "integer" + }, + "subject": { "type": "string", "maxLength": 200 }, - "body": { - "type": "string", - "maxLength": 12000 + "text_body": { + "type": "string" }, - "plan_id": { - "type": "integer", - "minimum": 1 + "html_body": { + "type": "string" + } + }, + "required": [ + "audience", + "html_body", + "key", + "stream", + "subject", + "text_body" + ] + }, + "EmailTemplatePreviewResponse": { + "type": "object", + "properties": { + "subject": { + "type": "string", + "readOnly": true }, - "status": { - "$ref": "#/components/schemas/BroadcastInputStatusEnum" + "text_body": { + "type": "string", + "readOnly": true }, - "certification_id": { - "type": "integer", - "minimum": 1 + "html_body": { + "type": "string", + "readOnly": true } }, "required": [ - "body", - "title" + "html_body", + "subject", + "text_body" ] }, - "BroadcastInputStatusEnum": { + "EmailVerificationConfirm": { + "type": "object", + "properties": { + "code": { + "type": "string", + "maxLength": 6 + } + }, + "required": [ + "code" + ] + }, + "EndReasonEnum": { "enum": [ - "active", - "lapsed" + "superseded", + "membership_revoked", + "claim_revoked", + "user_ended" ], "type": "string", - "description": "* `active` - active\n* `lapsed` - lapsed" + "description": "* `superseded` - Superseded\n* `membership_revoked` - Membership revoked\n* `claim_revoked` - Claim revoked\n* `user_ended` - User ended" }, - "BroadcastOptions": { + "EnrollmentGrant": { "type": "object", + "description": "Returned exactly once: the raw token is never stored and cannot be re-read.", "properties": { - "plans": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SegmentChoice" - } + "enrollment_id": { + "type": "string", + "format": "uuid" }, - "certifications": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SegmentChoice" - } + "device_id": { + "type": "string", + "format": "uuid" + }, + "purpose": { + "$ref": "#/components/schemas/EnrollmentGrantPurposeEnum" + }, + "token": { + "type": "string" + }, + "expires_at": { + "type": "string", + "format": "date-time" } }, "required": [ - "certifications", - "plans" + "device_id", + "enrollment_id", + "expires_at", + "purpose", + "token" ] }, - "BroadcastResult": { + "EnrollmentGrantPurposeEnum": { + "enum": [ + "enroll", + "rotate", + "reactivate" + ], + "type": "string", + "description": "* `enroll` - Enroll\n* `rotate` - Rotate\n* `reactivate` - Reactivate" + }, + "EnvironmentEnum": { + "enum": [ + "development", + "production" + ], + "type": "string", + "description": "* `development` - Development\n* `production` - Production" + }, + "Error": { "type": "object", "properties": { - "broadcast": { - "$ref": "#/components/schemas/Broadcast" - }, - "recipients": { - "type": "integer" + "detail": { + "type": "string" } - }, - "required": [ - "broadcast", - "recipients" - ] + } }, - "BulkImportJob": { + "EventAdmin": { "type": "object", "properties": { + "participating_organizations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ParticipatingOrganization" + }, + "readOnly": true + }, + "audience_label": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "organization_admission_blocked": { + "type": "boolean", + "readOnly": true + }, "id": { "type": "integer", "readOnly": true }, - "mode": { - "allOf": [ - { - "$ref": "#/components/schemas/Mode087Enum" - } - ], + "staff_permissions": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/BulkImportJobStatusEnum" - } - ], + "can_manage_staff": { + "type": "boolean", "readOnly": true }, - "total_rows": { + "makerspace_id": { "type": "integer", "readOnly": true }, - "processed_rows": { - "type": "integer", + "ticketing_features": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true }, - "created_count": { - "type": "integer", + "broadcasts_enabled": { + "type": "boolean", "readOnly": true }, - "updated_count": { - "type": "integer", + "report_features": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true }, - "error_count": { - "type": "integer", + "badge_sensitive_fields_allowed": { + "type": "boolean", "readOnly": true }, - "warning_count": { + "series_summary": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "integer" + }, + "public_token": { + "type": "string", + "format": "uuid" + }, + "title": { + "type": "string" + }, + "timezone": { + "type": "string" + } + }, + "readOnly": true + }, + "series_revision": { "type": "integer", + "readOnly": true, + "nullable": true + }, + "series_override_fields": { "readOnly": true }, - "result": { + "title": { + "type": "string", "readOnly": true }, - "error": { + "description": { "type": "string", "readOnly": true }, - "created_at": { + "starts_at": { "type": "string", "format": "date-time", "readOnly": true }, - "updated_at": { + "ends_at": { "type": "string", "format": "date-time", "readOnly": true }, - "completed_at": { + "timezone_name": { + "type": "string", + "readOnly": true + }, + "location": { + "type": "string", + "readOnly": true + }, + "location_kind": { + "allOf": [ + { + "$ref": "#/components/schemas/LocationKindEnum" + } + ], + "readOnly": true + }, + "custom_form": { + "readOnly": true, + "nullable": true + }, + "capacity": { + "type": "integer", + "readOnly": true + }, + "payment_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true + }, + "registration_requires_approval": { + "type": "boolean", + "readOnly": true + }, + "registration_audience": { + "allOf": [ + { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + } + ], + "readOnly": true + }, + "category_id": { + "type": "integer", + "nullable": true, + "readOnly": true + }, + "tags": { + "readOnly": true + }, + "cancellation_cutoff_hours": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "registration_cutoff_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true - } - }, - "required": [ - "completed_at", - "created_at", - "created_count", - "error", - "error_count", - "id", - "mode", - "processed_rows", - "result", - "status", - "total_rows", - "updated_at", - "updated_count", - "warning_count" - ] - }, - "BulkImportJobCreate": { - "type": "object", - "properties": { - "mode": { - "$ref": "#/components/schemas/Mode087Enum" }, - "file": { - "type": "string", - "format": "uri", + "registration_cutoff_lead_minutes": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "effective_registration_cutoff_at": { + "type": "string", + "format": "date-time", + "nullable": true, + "readOnly": true + }, + "registration_open": { + "type": "boolean", + "readOnly": true + }, + "offline_checkin_enabled": { + "type": "boolean", + "readOnly": true + }, + "is_public": { + "type": "boolean", + "readOnly": true + }, + "show_registration_count": { + "type": "boolean", + "readOnly": true + }, + "image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "readOnly": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusFbbEnum" + } + ], + "readOnly": true + }, + "created_by_id": { + "type": "integer", + "readOnly": true, "nullable": true }, - "rows": { + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "registration_counts": { + "allOf": [ + { + "$ref": "#/components/schemas/EventRegistrationCounts" + } + ], + "readOnly": true + }, + "organizers": { "type": "array", "items": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/EventOrganizerSummary" }, - "maxItems": 5000 - }, - "mapping": {} + "readOnly": true + } }, "required": [ - "mode" + "audience_label", + "badge_sensitive_fields_allowed", + "broadcasts_enabled", + "can_manage_staff", + "cancellation_cutoff_hours", + "capacity", + "category_id", + "created_at", + "created_by_id", + "custom_form", + "description", + "effective_registration_cutoff_at", + "ends_at", + "id", + "image_url", + "is_public", + "location", + "location_kind", + "makerspace_id", + "offline_checkin_enabled", + "organization_admission_blocked", + "organizers", + "participating_organizations", + "payment_amount", + "registration_audience", + "registration_counts", + "registration_cutoff_at", + "registration_cutoff_lead_minutes", + "registration_open", + "registration_requires_approval", + "report_features", + "series_override_fields", + "series_revision", + "series_summary", + "show_registration_count", + "staff_permissions", + "starts_at", + "status", + "tags", + "ticketing_features", + "timezone_name", + "title", + "updated_at" ] }, - "BulkImportJobStatusEnum": { - "enum": [ - "pending", - "running", - "completed", - "failed" - ], - "type": "string", - "description": "* `pending` - Pending\n* `running` - Running\n* `completed` - Completed\n* `failed` - Failed" - }, - "BulkImportPreview": { + "EventAttendanceMark": { "type": "object", "properties": { - "file": { - "type": "string", - "format": "uri", - "nullable": true - }, - "rows": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": {} - }, - "maxItems": 5000 - }, - "mapping": {} + "source": { + "allOf": [ + { + "$ref": "#/components/schemas/EventAttendanceMarkSourceEnum" + } + ], + "default": "online" + } } }, - "CadenceEnum": { + "EventAttendanceMarkSourceEnum": { "enum": [ - "daily", - "weekly", - "monthly" + "online", + "qr" ], "type": "string", - "description": "* `daily` - Daily\n* `weekly` - Weekly\n* `monthly` - Monthly" - }, - "Capability": { - "type": "object", - "properties": { - "value": { - "type": "string" - }, - "label": { - "type": "string" - }, - "description": { - "type": "string" - }, - "group": { - "type": "string" - }, - "grantable": { - "type": "boolean" - }, - "lock_reason": { - "type": "string", - "nullable": true - } - }, - "required": [ - "description", - "grantable", - "group", - "label", - "lock_reason", - "value" - ] + "description": "* `online` - online\n* `qr` - qr" }, - "CashBookReport": { + "EventAttendanceReport": { "type": "object", "properties": { "rows": { @@ -71506,7 +93324,7 @@ "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/CashPeriod" + "$ref": "#/components/schemas/EventAttendanceRow" } } }, @@ -71515,162 +93333,227 @@ "typed_rows" ] }, - "CashPeriod": { + "EventAttendanceResult": { + "oneOf": [ + { + "$ref": "#/components/schemas/EventRegistrationAdmin" + }, + { + "$ref": "#/components/schemas/EventCheckInResult" + } + ] + }, + "EventAttendanceRow": { "type": "object", "properties": { "makerspace_id": { "type": "integer" }, - "currency": { + "event_id": { + "type": "integer" + }, + "series_id": { + "type": "integer", + "nullable": true + }, + "series_title": { "type": "string" }, - "collected_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "series_occurrence_key": { + "type": "string" }, - "refunded_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "title": { + "type": "string" }, - "period": { + "starts_at": { "type": "string", - "format": "date" + "format": "date-time" }, - "method": { + "status": { "type": "string" }, - "net_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "capacity": { + "type": "integer" + }, + "registrations": { + "type": "integer" + }, + "confirmed": { + "type": "integer" + }, + "pending_approval": { + "type": "integer" + }, + "registered": { + "type": "integer" + }, + "waitlisted": { + "type": "integer" + }, + "rejected": { + "type": "integer" + }, + "cancelled": { + "type": "integer" + }, + "attended": { + "type": "integer" + }, + "attendance_rate_percent": { + "type": "number", + "format": "double", + "nullable": true + }, + "organizers": { + "type": "string" } }, "required": [ - "collected_amount", - "currency", - "method", - "net_amount", - "period", - "refunded_amount" + "attendance_rate_percent", + "attended", + "cancelled", + "capacity", + "confirmed", + "event_id", + "organizers", + "pending_approval", + "registered", + "registrations", + "rejected", + "series_id", + "series_occurrence_key", + "series_title", + "starts_at", + "status", + "title", + "waitlisted" ] }, - "Category": { + "EventBroadcast": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "name": { - "type": "string", + "event": { + "type": "integer", "readOnly": true }, - "slug": { + "subject": { "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" - } - }, - "required": [ - "id", - "name", - "slug" - ] - }, - "CategoryAdmin": { - "type": "object", - "properties": { - "id": { - "type": "integer", "readOnly": true }, - "makerspace": { - "type": "integer", + "body": { + "type": "string", "readOnly": true }, - "name": { - "type": "string", - "maxLength": 100 + "statuses": { + "readOnly": true }, - "slug": { - "oneOf": [ - { - "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" - }, + "attendance": { + "allOf": [ { - "type": "string", - "maxLength": 0 + "$ref": "#/components/schemas/EventBroadcastAttendanceEnum" } - ] - }, - "display_order": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1, - "nullable": true - }, - "max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0, - "nullable": true - }, - "icon": { - "type": "string", - "maxLength": 50 + ], + "readOnly": true }, - "product_count": { - "type": "integer", - "readOnly": true, - "default": 0 + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusAdeEnum" + } + ], + "readOnly": true }, "created_at": { "type": "string", "format": "date-time", "readOnly": true }, - "updated_at": { + "queued_at": { "type": "string", "format": "date-time", - "readOnly": true + "readOnly": true, + "nullable": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true } }, "required": [ + "attendance", + "body", + "completed_at", "created_at", + "event", "id", - "makerspace", - "name", - "product_count", - "updated_at" + "queued_at", + "status", + "statuses", + "subject" ] }, - "CategoryInput": { + "EventBroadcastAttendanceEnum": { + "enum": [ + "any", + "attended", + "not_attended" + ], + "type": "string", + "description": "* `any` - Any\n* `attended` - Attended\n* `not_attended` - Not attended" + }, + "EventBroadcastInput": { "type": "object", "properties": { - "name": { + "subject": { "type": "string", - "maxLength": 100 + "maxLength": 200 }, - "slug": { + "body": { "type": "string", - "maxLength": 100, - "pattern": "^[-a-zA-Z0-9_]+$" + "maxLength": 10000 + }, + "statuses": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StatusesDf6Enum" + }, + "default": [ + "registered", + "attended" + ], + "maxItems": 6, + "minItems": 1 + }, + "attendance": { + "allOf": [ + { + "$ref": "#/components/schemas/EventBroadcastInputAttendanceEnum" + } + ], + "default": "any" } }, "required": [ - "name", - "slug" + "body", + "subject" ] }, - "CategoryList": { + "EventBroadcastInputAttendanceEnum": { + "enum": [ + "any", + "attended", + "not_attended" + ], + "type": "string", + "description": "* `any` - any\n* `attended` - attended\n* `not_attended` - not_attended" + }, + "EventBroadcastList": { "type": "object", "properties": { "count": { @@ -71689,7 +93572,7 @@ "results": { "type": "array", "items": { - "$ref": "#/components/schemas/Category" + "$ref": "#/components/schemas/EventBroadcast" } } }, @@ -71700,1710 +93583,1915 @@ "results" ] }, - "CertificateDownload": { - "type": "object", - "properties": { - "url": { - "type": "string", - "format": "uri" - }, - "expires_at": { - "type": "string", - "format": "date-time" - } - }, - "required": [ - "expires_at", - "url" - ] - }, - "CertificateRevoke": { + "EventCheckInResolveRequest": { "type": "object", "properties": { - "reason": { - "$ref": "#/components/schemas/CertificateRevokeReasonEnum" + "checkin_token": { + "type": "string" } }, "required": [ - "reason" + "checkin_token" ] }, - "CertificateRevokeReasonEnum": { - "enum": [ - "staff_revoked" - ], - "type": "string", - "description": "* `staff_revoked` - staff_revoked" - }, - "CertificateSummary": { + "EventCheckInResolveResponse": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/CertificateSummaryStatusEnum" - } - ], - "readOnly": true + "registration_id": { + "type": "integer" }, - "revision": { - "type": "integer", - "readOnly": true + "name": { + "type": "string" }, - "issued_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "status": { + "type": "string" }, - "rendered_at": { + "payment_status": { "type": "string", - "format": "date-time", - "readOnly": true, "nullable": true }, - "revoked_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "host_waiver_state": { + "$ref": "#/components/schemas/HostWaiverStateEnum" + }, + "event_status": { + "type": "string" + }, + "confirmable": { + "type": "boolean" } }, "required": [ - "id", - "issued_at", - "rendered_at", - "revision", - "revoked_at", + "confirmable", + "event_status", + "host_waiver_state", + "name", + "payment_status", + "registration_id", "status" ] }, - "CertificateSummaryStatusEnum": { - "enum": [ - "pending", - "rendering", - "active", - "failed", - "revoked" - ], - "type": "string", - "description": "* `pending` - Pending\n* `rendering` - Rendering\n* `active` - Active\n* `failed` - Failed\n* `revoked` - Revoked" - }, - "CertificationGrant": { + "EventCheckInResult": { "type": "object", + "description": "Attendance operators need identity/state, never roster contacts or answers.", "properties": { "id": { "type": "integer", "readOnly": true }, - "certification_type": { - "type": "integer", - "readOnly": true - }, - "membership": { - "type": "integer", - "readOnly": true - }, - "member_name": { - "type": "string", - "readOnly": true - }, - "granted_by": { + "event_id": { "type": "integer", - "readOnly": true, - "nullable": true - }, - "granted_at": { - "type": "string", - "format": "date-time", "readOnly": true }, - "expires_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "revoked_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "revoked_by": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "notes": { + "name": { "type": "string", "readOnly": true }, - "is_live": { + "status": { "type": "string", "readOnly": true } }, "required": [ - "certification_type", - "expires_at", - "granted_at", - "granted_by", + "event_id", "id", - "is_live", - "member_name", - "membership", - "notes", - "revoked_at", - "revoked_by" + "name", + "status" ] }, - "CertificationGrantCreate": { + "EventCheckinTimesReport": { "type": "object", "properties": { - "membership_id": { - "type": "integer" - }, - "expires_at": { - "type": "string", - "format": "date-time", - "nullable": true + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "notes": { - "type": "string", - "default": "" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventCheckinTimesRow" + } } }, "required": [ - "membership_id" + "rows", + "typed_rows" ] }, - "CertificationGrantPage": { + "EventCheckinTimesRow": { "type": "object", "properties": { - "count": { + "makerspace_id": { "type": "integer" }, - "next": { + "period": { "type": "string", - "nullable": true + "format": "date-time" }, - "previous": { - "type": "string", - "nullable": true + "event_id": { + "type": "integer" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CertificationGrant" - } + "title": { + "type": "string" + }, + "timing_bucket": { + "type": "string" + }, + "checkin_count": { + "type": "integer" } }, "required": [ - "count", - "next", - "previous", - "results" + "checkin_count", + "event_id", + "period", + "timing_bucket", + "title" ] }, - "CertificationGrantRevoke": { - "type": "object", - "properties": { - "notes": { - "type": "string", - "default": "" - } - } - }, - "CertificationMember": { + "EventCollaborationInbox": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "name": { + "event_id": { + "type": "integer", + "readOnly": true + }, + "event_title": { + "type": "string", + "readOnly": true + }, + "starts_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "ends_at": { "type": "string", + "format": "date-time", + "readOnly": true + }, + "host_name": { + "type": "string", + "readOnly": true + }, + "host_slug": { + "type": "string", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusB9dEnum" + } + ], + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", "readOnly": true + }, + "responded_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true } }, "required": [ + "created_at", + "ends_at", + "event_id", + "event_title", + "host_name", + "host_slug", "id", - "name" + "responded_at", + "starts_at", + "status" ] }, - "CertificationMemberPage": { + "EventCollaborationRespond": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CertificationMember" - } + "accept": { + "type": "boolean" } }, "required": [ - "count", - "next", - "previous", - "results" + "accept" ] }, - "CertificationType": { + "EventCollaborator": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "machine_type": { - "type": "integer" + "event_id": { + "type": "integer", + "readOnly": true }, - "name": { + "makerspace_id": { + "type": "integer", + "readOnly": true + }, + "makerspace_name": { "type": "string", - "maxLength": 200 + "readOnly": true }, - "description": { - "type": "string" + "makerspace_slug": { + "type": "string", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "validity_days": { + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusB9dEnum" + } + ], + "readOnly": true + }, + "invited_by_id": { "type": "integer", - "maximum": 2147483647, - "minimum": 1, + "readOnly": true, "nullable": true }, - "is_required_for_service": { - "type": "boolean" - }, - "is_required_for_booking": { - "type": "boolean" - }, - "is_active": { - "type": "boolean", - "readOnly": true + "responded_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true }, "created_at": { "type": "string", "format": "date-time", "readOnly": true }, - "updated_at": { + "responded_at": { "type": "string", "format": "date-time", - "readOnly": true + "readOnly": true, + "nullable": true } }, "required": [ "created_at", + "event_id", "id", - "is_active", - "machine_type", - "name", - "updated_at" + "invited_by_id", + "makerspace_id", + "makerspace_name", + "makerspace_slug", + "responded_at", + "responded_by_id", + "status" ] }, - "CertificationTypePage": { + "EventCollaboratorReplace": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true - }, - "results": { + "slugs": { "type": "array", "items": { - "$ref": "#/components/schemas/CertificationType" + "type": "string", + "pattern": "^[-a-zA-Z0-9_]+$" } } }, "required": [ - "count", - "next", - "previous", - "results" + "slugs" ] }, - "ChainStatusEnum": { + "EventDefaultRegistrationAudienceEnum": { "enum": [ - "pending", - "locally_verified", - "invalid", - "unavailable" + "members", + "accounts", + "invite", + "participating_organizations" ], "type": "string", - "description": "* `pending` - Pending\n* `locally_verified` - Locally verified\n* `invalid` - Invalid\n* `unavailable` - Unavailable" + "description": "* `members` - Members\n* `accounts` - Authenticated accounts\n* `invite` - Invited accounts\n* `participating_organizations` - Participating organizations" }, - "ChallengeRequest": { + "EventDuplicate": { "type": "object", "properties": { - "token": { - "type": "string", - "maxLength": 256 - }, - "public_key": { - "type": "string", - "maxLength": 64 + "attach_series": { + "type": "boolean", + "default": false } - }, - "required": [ - "public_key", - "token" - ] + } }, - "ChallengeResponse": { + "EventEligibility": { "type": "object", "properties": { - "challenge": { - "type": "string" + "eligible": { + "type": "boolean", + "readOnly": true }, - "expires_at": { - "type": "string", - "format": "date-time" - } - }, - "required": [ - "challenge", - "expires_at" - ] - }, - "ChangePassword": { - "type": "object", - "properties": { - "current_password": { + "reason": { "type": "string", - "writeOnly": true + "readOnly": true, + "nullable": true }, - "new_password": { - "type": "string", - "writeOnly": true + "consent_repair_required": { + "type": "boolean", + "readOnly": true + }, + "host_waiver": { + "allOf": [ + { + "$ref": "#/components/schemas/HostWaiverProjection" + } + ], + "readOnly": true, + "nullable": true } }, "required": [ - "current_password", - "new_password" + "consent_repair_required", + "eligible", + "host_waiver", + "reason" ] }, - "ChangePasswordResponse": { + "EventEligibleMember": { "type": "object", + "description": "A picker row. Name and id only — a roster is not a contact export.", "properties": { - "detail": { + "member_id": { + "type": "integer", + "minimum": 1 + }, + "display_name": { "type": "string" } }, "required": [ - "detail" + "display_name", + "member_id" ] }, - "Channel59dEnum": { - "enum": [ - "telegram", - "slack", - "mattermost", - "discord", - "webhook" - ], - "type": "string", - "description": "* `telegram` - Telegram\n* `slack` - Slack\n* `mattermost` - Mattermost\n* `discord` - Discord\n* `webhook` - Signed webhook" - }, - "Channel673Enum": { - "enum": [ - "email", - "in_app", - "native_push" - ], - "type": "string", - "description": "* `email` - email\n* `in_app` - in_app\n* `native_push` - native_push" - }, - "ChannelB30Enum": { - "enum": [ - "in_app", - "email", - "telegram", - "slack", - "mattermost", - "discord", - "webhook", - "native_push" - ], - "type": "string", - "description": "* `in_app` - Inbox\n* `email` - Email\n* `telegram` - Telegram\n* `slack` - Slack\n* `mattermost` - Mattermost\n* `discord` - Discord\n* `webhook` - Signed webhook\n* `native_push` - Native push" - }, - "ChannelsEnum": { - "enum": [ - "email", - "telegram", - "slack", - "mattermost", - "discord", - "native_push", - "webhook" - ], - "type": "string", - "description": "* `email` - email\n* `telegram` - telegram\n* `slack` - slack\n* `mattermost` - mattermost\n* `discord` - discord\n* `native_push` - native_push\n* `webhook` - webhook" - }, - "CheckoutUrl": { + "EventFeedbackReport": { "type": "object", "properties": { - "checkout_url": { - "type": "string", - "format": "uri" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventFeedbackReportRow" + } } }, "required": [ - "checkout_url" + "rows", + "typed_rows" ] }, - "ClaimRedemption": { + "EventFeedbackReportRow": { "type": "object", "properties": { - "code": { - "type": "string", - "writeOnly": true, - "maxLength": 64 + "response_id": { + "type": "integer" }, - "makerspace_slug": { + "created_at": { "type": "string", - "writeOnly": true, - "maxLength": 80, - "pattern": "^[-a-zA-Z0-9_]+$" + "format": "date-time" + }, + "registration_id": { + "type": "integer", + "nullable": true + }, + "answers": { + "type": "string" } }, "required": [ - "code", - "makerspace_slug" + "answers", + "created_at", + "registration_id", + "response_id" ] }, - "ClaimRedemptionResponse": { + "EventFeedbackScoresReport": { "type": "object", "properties": { - "user": { - "$ref": "#/components/schemas/AuthUserPayload" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "access": { - "type": "string" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventFeedbackScoresRow" + } } }, "required": [ - "access", - "user" + "rows", + "typed_rows" ] }, - "ClaimableInvitation": { + "EventFeedbackScoresRow": { "type": "object", "properties": { - "id": { + "makerspace_id": { "type": "integer" }, - "makerspace": { - "type": "object", - "additionalProperties": {} - }, - "inviter": { + "period": { "type": "string", - "nullable": true + "format": "date-time" }, - "auto_activates": { - "type": "boolean" + "event_id": { + "type": "integer" }, - "is_staff": { - "type": "boolean" + "title": { + "type": "string" }, - "role": { + "question_id": { + "type": "string" + }, + "score_count": { + "type": "integer" + }, + "score_total": { "type": "string", - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + }, + "average_score": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" } }, "required": [ - "auto_activates", - "id", - "inviter", - "is_staff", - "makerspace", - "role" + "average_score", + "event_id", + "period", + "question_id", + "score_count", + "score_total", + "title" ] }, - "ClientPlatformEnum": { - "enum": [ - "web", - "ios", - "android" - ], - "type": "string", - "description": "* `web` - Web\n* `ios` - iOS\n* `android` - Android" - }, - "ClientTypeEnum": { - "enum": [ - "browser", - "server" - ], - "type": "string", - "description": "* `browser` - Browser\n* `server` - Server" + "EventFunnelReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventFunnelRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] }, - "ClosureApproval": { + "EventFunnelRow": { "type": "object", "properties": { - "id": { + "makerspace_id": { + "type": "integer" + }, + "period": { "type": "string", - "format": "uuid", - "readOnly": true + "format": "date-time" + }, + "event_id": { + "type": "integer" }, - "closure_digest": { - "type": "string", - "maxLength": 64 + "title": { + "type": "string" }, - "identity_count": { - "type": "string", - "readOnly": true + "registrations": { + "type": "integer" }, - "approved_count": { - "type": "string", - "readOnly": true + "confirmed": { + "type": "integer" }, - "approved_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "attended": { + "type": "integer" }, - "revoked_at": { - "type": "string", - "format": "date-time", + "confirmation_rate_percent": { + "type": "number", + "format": "double", + "nullable": true + }, + "attendance_rate_percent": { + "type": "number", + "format": "double", "nullable": true } }, "required": [ - "approved_at", - "approved_count", - "closure_digest", - "id", - "identity_count" + "attendance_rate_percent", + "attended", + "confirmation_rate_percent", + "confirmed", + "event_id", + "period", + "registrations", + "title" ] }, - "ClosureApprovalCreate": { + "EventListResponse": { "type": "object", "properties": { - "digest": { + "count": { + "type": "integer" + }, + "next": { "type": "string", - "maxLength": 64, - "minLength": 64 + "nullable": true }, - "decisions": { + "previous": { + "type": "string", + "nullable": true + }, + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/IdentityDisclosureDecision" + "$ref": "#/components/schemas/EventAdmin" } } }, "required": [ - "decisions", - "digest" + "count", + "results" ] }, - "ClosureIdentity": { + "EventListWithDefaults": { "type": "object", "properties": { - "id": { + "count": { "type": "integer" }, - "username": { - "type": "string" - }, - "email": { - "oneOf": [ - { - "type": "string", - "format": "email" - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "first_name": { - "type": "string" - }, - "last_name": { - "type": "string" + "next": { + "type": "string", + "nullable": true }, - "display_name": { - "type": "string" + "previous": { + "type": "string", + "nullable": true }, - "phone": { - "type": "string" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventAdmin" + } }, - "date_joined": { - "type": "string", - "format": "date-time" + "default_registration_audience": { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" } }, "required": [ - "date_joined", - "display_name", - "email", - "first_name", - "id", - "last_name", - "phone", - "username" + "count", + "default_registration_audience", + "results" ] }, - "CollaborativeEvent": { + "EventOrganizerList": { "type": "object", "properties": { - "registration_audience": { - "allOf": [ - { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" - } - ], - "readOnly": true - }, - "category": { - "type": "object", - "nullable": true, - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "slug": { - "type": "string" - } - }, - "readOnly": true - }, - "tags": { + "organizers": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/EventOrganizerSummary" }, "readOnly": true - }, - "ticket_types": { + } + }, + "required": [ + "organizers" + ] + }, + "EventOrganizerReplace": { + "type": "object", + "properties": { + "organization_ids": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "audience": { - "type": "string" - }, - "spots_left": { - "type": "integer", - "nullable": true - } - } + "type": "integer", + "minimum": 1 }, + "maxItems": 50 + } + }, + "required": [ + "organization_ids" + ] + }, + "EventOrganizerSummary": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "promo_codes_enabled": { - "type": "boolean", - "readOnly": true - }, - "ticket_selection_required": { - "type": "boolean", - "readOnly": true - }, - "payment_amount": { + "slug": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "currency": { + "name": { "type": "string", "readOnly": true + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "EventRegistrationAdmin": { + "type": "object", + "properties": { + "no_longer_eligible": { + "type": "boolean", + "readOnly": true, + "default": false }, "id": { "type": "integer", "readOnly": true }, - "title": { - "type": "string", + "event_id": { + "type": "integer", "readOnly": true }, - "description": { + "name": { "type": "string", "readOnly": true }, - "starts_at": { + "email": { "type": "string", - "format": "date-time", "readOnly": true }, - "ends_at": { + "phone": { "type": "string", - "format": "date-time", "readOnly": true }, - "location": { - "type": "string", - "readOnly": true + "custom_answers": { + "readOnly": true, + "nullable": true }, - "location_kind": { + "status": { "allOf": [ { - "$ref": "#/components/schemas/LocationKindEnum" + "$ref": "#/components/schemas/StatusDf6Enum" } ], "readOnly": true }, - "custom_form": { - "readOnly": true, - "nullable": true - }, - "capacity": { - "type": "integer", - "minimum": 0, + "created_at": { + "type": "string", + "format": "date-time", "readOnly": true }, - "availability": { + "payment": { "allOf": [ { - "$ref": "#/components/schemas/AvailabilityEnum" + "$ref": "#/components/schemas/StaffPaymentSummary" } ], - "readOnly": true - }, - "registration_requires_approval": { - "type": "boolean", - "readOnly": true - }, - "effective_registration_cutoff_at": { - "type": "string", - "format": "date-time", "nullable": true, "readOnly": true }, - "registration_open": { - "type": "boolean", - "readOnly": true + "ticket_type_id": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "image_url": { + "quoted_amount": { "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true, + "nullable": true }, - "host_name": { + "discount_amount": { "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "host_slug": { + "cancelled_at": { "type": "string", + "format": "date-time", "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "host_waiver": { - "allOf": [ - { - "$ref": "#/components/schemas/HostWaiver" - } - ], - "nullable": true, - "readOnly": true + "nullable": true }, - "organizers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventOrganizerSummary" - }, + "status_before_cancel": { + "type": "string", "readOnly": true }, - "series": { - "type": "object", + "restore_until": { + "type": "string", + "format": "date-time", "nullable": true, "readOnly": true } }, "required": [ - "availability", - "capacity", - "category", - "currency", - "custom_form", - "description", - "effective_registration_cutoff_at", - "ends_at", - "host_name", - "host_slug", - "host_waiver", + "cancelled_at", + "created_at", + "custom_answers", + "discount_amount", + "email", + "event_id", "id", - "image_url", - "location", - "location_kind", - "organizers", - "payment_amount", - "promo_codes_enabled", - "registration_audience", - "registration_open", - "registration_requires_approval", - "series", - "starts_at", - "tags", - "ticket_selection_required", - "ticket_types", - "title" + "name", + "no_longer_eligible", + "payment", + "phone", + "quoted_amount", + "restore_until", + "status", + "status_before_cancel", + "ticket_type_id" ] }, - "CollaborativeEventRegistrationInput": { + "EventRegistrationCounts": { "type": "object", "properties": { - "custom_answers": { - "writeOnly": true, - "nullable": true - }, - "phone": { - "type": "string", - "writeOnly": true, - "maxLength": 64 + "pending_approval": { + "type": "integer", + "readOnly": true }, - "email": { - "writeOnly": true, - "oneOf": [ - { - "type": "string", - "format": "email" - }, - { - "type": "string", - "maxLength": 0 - } - ] + "registered": { + "type": "integer", + "readOnly": true }, - "invite_code": { - "type": "string", - "writeOnly": true, - "maxLength": 128 + "waitlisted": { + "type": "integer", + "readOnly": true }, - "ticket_type_id": { + "rejected": { "type": "integer", - "minimum": 1, - "writeOnly": true + "readOnly": true }, - "promo_code": { - "type": "string", - "writeOnly": true, - "maxLength": 128 + "cancelled": { + "type": "integer", + "readOnly": true }, - "host_waiver_id": { + "attended": { "type": "integer", - "minimum": 1, + "readOnly": true + } + }, + "required": [ + "attended", + "cancelled", + "pending_approval", + "registered", + "rejected", + "waitlisted" + ] + }, + "EventRegistrationListResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", "nullable": true }, - "host_waiver_version": { + "previous": { "type": "string", - "nullable": true, - "maxLength": 64 + "nullable": true }, - "host_waiver_accepted": { - "type": "boolean", - "default": false + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventRegistrationAdmin" + } } - } + }, + "required": [ + "count", + "results" + ] }, - "CommandAck": { + "EventRegistrationReport": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventRegistrationReportRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "EventRegistrationReportRow": { + "type": "object", + "properties": { + "registration_id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "phone": { + "type": "string" }, "status": { "type": "string" }, - "acknowledged_at": { + "created_at": { "type": "string", - "format": "date-time", - "nullable": true + "format": "date-time" + }, + "answers": { + "type": "string" } }, "required": [ - "acknowledged_at", - "id", + "answers", + "created_at", + "email", + "name", + "phone", + "registration_id", "status" ] }, - "CommandLease": { + "EventRevenueReport": { "type": "object", "properties": { - "command_id": { - "type": "string", - "format": "uuid" - }, - "kind": { - "type": "string" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "lease_token": { - "type": "string" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventRevenueRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "EventRevenueRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "lease_expires_at": { + "period": { "type": "string", "format": "date-time" }, - "printer_job_id": { - "type": "string", - "format": "uuid" + "event_id": { + "type": "integer" }, - "external_job_id": { + "title": { "type": "string" }, - "service_request_id": { + "currency": { + "type": "string" + }, + "charge_count": { "type": "integer" }, - "label_command_id": { + "charged_amount": { "type": "string", - "format": "uuid" + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" }, - "qr_batch_id": { - "type": "integer" + "promo_discount_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" }, - "item_count": { - "type": "integer" - } - }, - "required": [ - "command_id", - "kind", - "lease_expires_at", - "lease_token" - ] - }, - "CompleteRequest": { - "type": "object", - "properties": { - "token": { + "collected_amount": { "type": "string", - "maxLength": 256 + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" }, - "public_key": { + "refunded_amount": { "type": "string", - "maxLength": 64 + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" }, - "challenge": { + "outstanding_amount": { "type": "string", - "maxLength": 64 + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" }, - "signature": { + "net_amount": { "type": "string", - "maxLength": 128 + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" } }, "required": [ - "challenge", - "public_key", - "signature", - "token" + "charge_count", + "charged_amount", + "collected_amount", + "currency", + "event_id", + "net_amount", + "outstanding_amount", + "period", + "promo_discount_amount", + "refunded_amount", + "title" ] }, - "ConditionEnum": { - "enum": [ - "available", - "damaged", - "lost", - "unknown" - ], - "type": "string", - "description": "* `available` - Available\n* `damaged` - Damaged\n* `lost` - Lost\n* `unknown` - Unknown" - }, - "ConnectStatusEnum": { - "enum": [ - "unconnected", - "pending", - "active", - "restricted", - "disconnected" - ], - "type": "string", - "description": "* `unconnected` - Unconnected\n* `pending` - Pending\n* `active` - Active\n* `restricted` - Restricted\n* `disconnected` - Disconnected" - }, - "ConnectedDeviceIdentity": { + "EventSeriesDetail": { "type": "object", "properties": { - "device_id": { + "id": { + "type": "integer", + "readOnly": true + }, + "staff_permissions": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "can_manage_staff": { + "type": "boolean", + "readOnly": true + }, + "public_token": { "type": "string", - "format": "uuid" + "format": "uuid", + "readOnly": true }, "makerspace_id": { - "type": "integer" + "type": "integer", + "readOnly": true + }, + "title": { + "type": "string", + "readOnly": true }, "status": { - "type": "string" + "allOf": [ + { + "$ref": "#/components/schemas/StatusFbbEnum" + } + ], + "readOnly": true }, - "key_version": { - "type": "integer" + "recurrence_timezone": { + "type": "string", + "readOnly": true }, - "last_seen_at": { + "dtstart_local_date": { "type": "string", - "format": "date-time", - "nullable": true - } - }, - "required": [ - "device_id", - "key_version", - "last_seen_at", - "makerspace_id", - "status" - ] - }, - "ConsumableCandidate": { - "type": "object", - "properties": { - "id": { + "format": "date", + "readOnly": true + }, + "dtstart_local_time": { + "type": "string", + "format": "time", + "readOnly": true + }, + "recurrence_rule": { + "type": "string", + "readOnly": true + }, + "duration_minutes": { "type": "integer", "readOnly": true }, - "name": { + "revision": { + "type": "integer", + "readOnly": true + }, + "next_occurrence_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "available": { + "future_occurrence_count": { "type": "integer", "readOnly": true - } - }, - "required": [ - "available", - "id", - "name" - ] - }, - "Consumption": { - "type": "object", - "properties": { - "machine_consumable_id": { + }, + "last_materialized_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_generation_error_code": { + "type": "string", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "participating_organizations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ParticipatingOrganization" + }, + "readOnly": true + }, + "audience_label": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "organization_admission_blocked": { + "type": "boolean", + "readOnly": true + }, + "organization_audience_enabled": { + "type": "boolean", + "readOnly": true + }, + "website_enabled": { + "type": "boolean", + "readOnly": true + }, + "description": { + "type": "string" + }, + "location": { + "type": "string", + "maxLength": 255 + }, + "location_kind": { + "$ref": "#/components/schemas/LocationKindEnum" + }, + "custom_form": { + "nullable": true + }, + "capacity": { "type": "integer", - "minimum": 1 + "maximum": 2147483647, + "minimum": 0 }, - "quantity": { + "payment_amount": { "type": "string", "format": "decimal", "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "registration_requires_approval": { + "type": "boolean" + }, + "registration_audience": { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + }, + "cancellation_cutoff_hours": { + "type": "integer", + "maximum": 8760, + "minimum": 0, + "nullable": true + }, + "registration_cutoff_lead_minutes": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "is_public": { + "type": "boolean" + }, + "created_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "image_url": { + "type": "string", + "format": "uri", + "readOnly": true } }, "required": [ - "machine_consumable_id", - "quantity" + "audience_label", + "can_manage_staff", + "created_at", + "created_by_id", + "dtstart_local_date", + "dtstart_local_time", + "duration_minutes", + "future_occurrence_count", + "id", + "image_url", + "last_generation_error_code", + "last_materialized_at", + "makerspace_id", + "next_occurrence_at", + "organization_admission_blocked", + "organization_audience_enabled", + "participating_organizations", + "public_token", + "recurrence_rule", + "recurrence_timezone", + "revision", + "staff_permissions", + "status", + "title", + "updated_at", + "website_enabled" ] }, - "ContainerAssetSummary": { + "EventSeriesListResponse": { "type": "object", "properties": { - "id": { + "count": { "type": "integer" }, - "asset_tag": { - "type": "string" + "next": { + "type": "string", + "nullable": true }, - "product": { - "type": "string" + "previous": { + "type": "string", + "nullable": true }, - "status": { - "type": "string" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventSeriesSummary" + } } }, "required": [ - "asset_tag", - "id", - "product", - "status" + "count", + "next", + "previous", + "results" ] }, - "ContainerContents": { + "EventSeriesMutationResponse": { "type": "object", "properties": { - "container": { - "$ref": "#/components/schemas/Box" + "series": { + "allOf": [ + { + "$ref": "#/components/schemas/EventSeriesDetail" + } + ], + "readOnly": true }, - "products": { + "created_occurrence_ids": { "type": "array", "items": { - "$ref": "#/components/schemas/ContainerProductSummary" - } + "type": "integer" + }, + "readOnly": true }, - "assets": { + "removed_occurrence_ids": { "type": "array", "items": { - "$ref": "#/components/schemas/ContainerAssetSummary" - } + "type": "integer" + }, + "readOnly": true }, - "children": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Box" - } + "affected_count": { + "type": "integer", + "readOnly": true } }, "required": [ - "assets", - "children", - "container", - "products" + "affected_count", + "created_occurrence_ids", + "removed_occurrence_ids", + "series" ] }, - "ContainerHistory": { + "EventSeriesSummary": { "type": "object", "properties": { - "container": { - "type": "integer" + "id": { + "type": "integer", + "readOnly": true }, - "scans": { + "staff_permissions": { "type": "array", "items": { - "$ref": "#/components/schemas/ContainerScanHistoryItem" - } + "type": "string" + }, + "readOnly": true + }, + "can_manage_staff": { + "type": "boolean", + "readOnly": true + }, + "public_token": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "makerspace_id": { + "type": "integer", + "readOnly": true + }, + "title": { + "type": "string", + "readOnly": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusFbbEnum" + } + ], + "readOnly": true + }, + "recurrence_timezone": { + "type": "string", + "readOnly": true + }, + "dtstart_local_date": { + "type": "string", + "format": "date", + "readOnly": true + }, + "dtstart_local_time": { + "type": "string", + "format": "time", + "readOnly": true + }, + "recurrence_rule": { + "type": "string", + "readOnly": true + }, + "duration_minutes": { + "type": "integer", + "readOnly": true + }, + "revision": { + "type": "integer", + "readOnly": true + }, + "next_occurrence_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "future_occurrence_count": { + "type": "integer", + "readOnly": true + }, + "last_materialized_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_generation_error_code": { + "type": "string", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ - "container", - "scans" + "can_manage_staff", + "dtstart_local_date", + "dtstart_local_time", + "duration_minutes", + "future_occurrence_count", + "id", + "last_generation_error_code", + "last_materialized_at", + "makerspace_id", + "next_occurrence_at", + "public_token", + "recurrence_rule", + "recurrence_timezone", + "revision", + "staff_permissions", + "status", + "title", + "updated_at" ] }, - "ContainerMove": { + "EventSeriesWrite": { "type": "object", "properties": { - "kit_components": { + "registration_audience": { + "$ref": "#/components/schemas/EventSeriesWriteRegistrationAudienceEnum" + }, + "ticket_types": { "type": "array", "items": { - "$ref": "#/components/schemas/KitComponent" + "$ref": "#/components/schemas/TicketInput" }, - "maxItems": 50 + "writeOnly": true }, - "parent_id": { - "type": "integer", + "title": { + "type": "string", + "maxLength": 200 + }, + "description": { + "type": "string", + "default": "" + }, + "location": { + "type": "string", + "default": "", + "maxLength": 255 + }, + "location_kind": { + "allOf": [ + { + "$ref": "#/components/schemas/LocationKindEnum" + } + ], + "default": "other" + }, + "custom_form": { "nullable": true }, - "label": { - "type": "string" + "capacity": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "payment_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "default": "0.00" }, - "location": { - "type": "string" + "registration_requires_approval": { + "type": "boolean", + "default": false }, - "description": { - "type": "string" + "cancellation_cutoff_hours": { + "type": "integer", + "maximum": 8760, + "minimum": 0, + "nullable": true }, - "is_active": { - "type": "boolean" - } - } - }, - "ContainerProductSummary": { - "type": "object", - "properties": { - "id": { - "type": "integer" + "registration_cutoff_lead_minutes": { + "type": "integer", + "minimum": 0, + "nullable": true }, - "name": { - "type": "string" + "is_public": { + "type": "boolean", + "default": false }, - "available_quantity": { - "type": "integer" + "recurrence_timezone": { + "type": "string", + "maxLength": 64 }, - "tracking_mode": { - "type": "string" - } - }, - "required": [ - "available_quantity", - "id", - "name", - "tracking_mode" - ] - }, - "ContainerScanHistoryItem": { - "type": "object", - "properties": { - "id": { - "type": "string" + "dtstart_local_date": { + "type": "string", + "format": "date" }, - "source": { - "type": "string" + "dtstart_local_time": { + "type": "string", + "format": "time" }, - "context": { - "type": "string" + "recurrence_rule": { + "type": "string", + "maxLength": 500 }, - "actor": { + "duration_minutes": { "type": "integer", - "nullable": true + "minimum": 1 }, - "created_at": { + "effective_from": { "type": "string", - "format": "date-time" + "format": "date-time", + "writeOnly": true } }, "required": [ - "actor", - "context", - "created_at", - "id", - "source" + "dtstart_local_date", + "dtstart_local_time", + "duration_minutes", + "recurrence_rule", + "recurrence_timezone", + "title" ] }, - "ContextEnum": { + "EventSeriesWriteRegistrationAudienceEnum": { "enum": [ - "issue", - "return", - "inventory_check" + "members", + "accounts", + "invite", + "participating_organizations" ], "type": "string", - "description": "* `issue` - issue\n* `return` - return\n* `inventory_check` - inventory_check" + "description": "* `members` - members\n* `accounts` - accounts\n* `invite` - invite\n* `participating_organizations` - participating_organizations" }, - "Count": { + "EventSignupsReport": { "type": "object", "properties": { - "count": { - "type": "integer" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventSignupsRow" + } } }, "required": [ - "count" + "rows", + "typed_rows" ] }, - "CreateBoxQr": { + "EventSignupsRow": { "type": "object", "properties": { "makerspace_id": { "type": "integer" }, - "label": { - "type": "string" + "period": { + "type": "string", + "format": "date-time" }, - "location": { - "type": "string" + "event_id": { + "type": "integer" }, - "description": { + "title": { "type": "string" }, - "parent_id": { - "type": "integer", - "nullable": true + "registrations": { + "type": "integer" + }, + "cumulative_registrations": { + "type": "integer" } }, "required": [ - "label", - "makerspace_id" + "cumulative_registrations", + "event_id", + "period", + "registrations", + "title" ] }, - "CreateToolQr": { + "EventStaffRegistration": { "type": "object", + "description": "Staff registering an identified account under the event's audience rules.\n\n`member_id` only. Contact details are copied off the account by the registration\nservice, so a staffer cannot record an attendee under a name and email that belong\nto nobody — which is what makes the attendee list usable as an accountability record\nrather than free text.", "properties": { - "makerspace_id": { - "type": "integer" + "member_id": { + "type": "integer", + "minimum": 1 }, - "product_id": { - "type": "integer" + "invite_code": { + "type": "string", + "writeOnly": true, + "maxLength": 128 }, - "asset_id": { - "type": "integer" + "ticket_type_id": { + "type": "integer", + "minimum": 1 + }, + "promo_code": { + "type": "string", + "writeOnly": true, + "maxLength": 128 + }, + "custom_answers": { + "nullable": true + }, + "phone": { + "type": "string", + "default": "", + "maxLength": 32 + }, + "email": { + "oneOf": [ + { + "type": "string", + "format": "email", + "default": "" + }, + { + "type": "string", + "maxLength": 0 + } + ] } }, "required": [ - "makerspace_id" + "member_id" ] }, - "CreationInviteDelivery": { + "EventWebsitePublishInput": { "type": "object", "properties": { - "accept_url": { - "type": "string" + "publish_website": { + "type": "boolean", + "default": false }, - "delivery_status": { - "type": "string" + "website_revision": { + "type": "integer", + "minimum": 1 } - }, - "required": [ - "accept_url", - "delivery_status" - ] + } }, - "CreationProfile": { + "EventWrite": { "type": "object", - "description": "Explicit fields override a pack; omitted fields keep its configuration.", "properties": { - "timezone": { + "registration_audience": { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + }, + "category_id": { + "type": "integer", + "minimum": 1, + "nullable": true + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "maxLength": 50 + }, + "maxItems": 20 + }, + "title": { "type": "string", - "maxLength": 100 + "maxLength": 200 }, - "country": { + "description": { "type": "string", - "maxLength": 2 + "default": "" }, - "opening_hours": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + }, + "timezone_name": { + "type": "string", + "maxLength": 64 + }, + "location": { + "type": "string", + "default": "", + "maxLength": 255 + }, + "location_kind": { + "allOf": [ { - "type": "object", - "required": [ - "weekly", - "exceptions" - ], - "additionalProperties": false, - "properties": { - "weekly": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "additionalProperties": false, - "required": [ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun" - ], - "properties": { - "mon": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "tue": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "wed": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "thu": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "fri": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sat": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sun": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - } - } - } - ] - }, - "exceptions": { - "type": "object", - "maxProperties": 366, - "additionalProperties": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." - } - } + "$ref": "#/components/schemas/LocationKindEnum" } ], - "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." + "default": "other" }, - "default_currency": { + "custom_form": { + "nullable": true + }, + "capacity": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "payment_amount": { "type": "string", - "maxLength": 3 + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "default": "0.00" + }, + "is_public": { + "type": "boolean", + "default": false + }, + "show_registration_count": { + "type": "boolean", + "default": true + }, + "registration_requires_approval": { + "type": "boolean", + "default": false + }, + "cancellation_cutoff_hours": { + "type": "integer", + "maximum": 8760, + "minimum": 0, + "nullable": true, + "description": "Member cancellation closes this many hours before start; null means start." + }, + "registration_cutoff_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "registration_cutoff_lead_minutes": { + "type": "integer", + "minimum": 0, + "nullable": true }, - "timezone_choices": { + "inherit_fields": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/InheritFieldsEnum" }, - "readOnly": true + "writeOnly": true + } + }, + "required": [ + "ends_at", + "starts_at", + "title" + ] + }, + "EvidenceGetResponse": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" }, - "country_choices": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true + "expires_in": { + "type": "integer" } }, "required": [ - "country_choices", - "timezone_choices" + "expires_in", + "url" ] }, - "CustodyState": { + "EvidenceRetention": { "type": "object", + "description": "Retention coordination only: ``null`` on the event means no state row exists,\nwhich is NOT a claim that the bytes are available.", "properties": { "state": { - "type": "string" - }, - "reason_code": { - "type": "string" - }, - "entered_at": { - "type": "string", - "format": "date-time", - "nullable": true + "$ref": "#/components/schemas/EvidenceRetentionStateEnum" }, - "cleared_at": { + "object_expired_at": { "type": "string", "format": "date-time", "nullable": true } }, "required": [ - "cleared_at", - "entered_at", - "reason_code", + "object_expired_at", "state" ] }, - "CutoverOutcome": { + "EvidenceRetentionPolicy": { "type": "object", "properties": { - "message": { - "type": "string" + "makerspace_id": { + "type": "integer" }, - "receipt": { - "$ref": "#/components/schemas/ReceiptEnvelope" + "platform_default_days": { + "type": "integer" + }, + "override_days": { + "type": "integer", + "nullable": true + }, + "effective_days": { + "type": "integer" + }, + "object_expiry_enabled": { + "type": "boolean" + }, + "abandoned_upload_cleanup_enabled": { + "type": "boolean" + }, + "abandoned_upload_platform_default_days": { + "type": "integer" + }, + "abandoned_upload_override_days": { + "type": "integer", + "nullable": true + }, + "abandoned_upload_effective_days": { + "type": "integer" + }, + "abandoned_upload_global_enabled": { + "type": "boolean" } }, "required": [ - "message" + "abandoned_upload_cleanup_enabled", + "abandoned_upload_effective_days", + "abandoned_upload_global_enabled", + "abandoned_upload_override_days", + "abandoned_upload_platform_default_days", + "effective_days", + "makerspace_id", + "object_expiry_enabled", + "override_days", + "platform_default_days" ] }, - "CutoverReceiptRequest": { + "EvidenceRetentionPreviewRequest": { "type": "object", "properties": { - "receipt": { - "$ref": "#/components/schemas/ReceiptEnvelope" + "limit": { + "type": "integer", + "maximum": 1000, + "minimum": 1, + "default": 100 + } + } + }, + "EvidenceRetentionPreviewResponse": { + "type": "object", + "properties": { + "as_of": { + "type": "string", + "format": "date-time" + }, + "policy_days": { + "type": "integer" + }, + "cutoff": { + "type": "string", + "format": "date-time" + }, + "object_candidates": { + "type": "integer" + }, + "candidate_bytes": { + "type": "integer" + }, + "has_more": { + "type": "boolean" + }, + "abandoned_upload_cutoff": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "abandoned_upload_candidates": { + "type": "integer" + }, + "abandoned_upload_candidate_bytes": { + "type": "integer" + }, + "abandoned_upload_has_more": { + "type": "boolean" + }, + "late_write_receipts": { + "type": "integer" + }, + "late_write_receipts_due": { + "type": "integer" } }, "required": [ - "receipt" + "abandoned_upload_candidate_bytes", + "abandoned_upload_candidates", + "abandoned_upload_cutoff", + "abandoned_upload_has_more", + "as_of", + "candidate_bytes", + "cutoff", + "has_more", + "late_write_receipts", + "late_write_receipts_due", + "object_candidates", + "policy_days" ] }, - "DamagedLostReport": { + "EvidenceRetentionStateEnum": { + "enum": [ + "expiring", + "expired" + ], + "type": "string", + "description": "* `expiring` - expiring\n* `expired` - expired" + }, + "EvidenceType740Enum": { + "enum": [ + "issue", + "return" + ], + "type": "string", + "description": "* `issue` - issue\n* `return` - return" + }, + "EvidenceUrlRequest": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "evidence_type": { + "$ref": "#/components/schemas/EvidenceUrlRequestEvidenceTypeEnum" }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DamagedLostReportRow" - } + "content_type": { + "type": "string" + }, + "size_bytes": { + "type": "integer", + "minimum": 0, + "nullable": true } }, "required": [ - "rows", - "typed_rows" + "content_type", + "evidence_type" ] }, - "DamagedLostReportRow": { + "EvidenceUrlRequestEvidenceTypeEnum": { + "enum": [ + "issue", + "return" + ], + "type": "string", + "description": "* `issue` - Issue\n* `return` - Return" + }, + "EvidenceUrlResponse": { "type": "object", "properties": { - "makerspace_id": { + "evidence_id": { "type": "integer" }, - "product_name": { + "upload_url": { + "type": "string", + "format": "uri" + }, + "fields": { + "type": "object", + "additionalProperties": {} + }, + "object_key": { "type": "string" }, - "damaged_quantity": { - "type": "integer" + "method": { + "type": "string" }, - "lost_quantity": { - "type": "integer" + "headers": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "damaged_quantity", - "lost_quantity", - "product_name" + "evidence_id", + "fields", + "object_key", + "upload_url" ] }, - "DamagedMissingReport": { + "FabLabHealthReport": { "type": "object", "properties": { "rows": { @@ -73416,7 +95504,7 @@ "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/DamagedMissingReportRow" + "$ref": "#/components/schemas/FabLabHealthRow" } } }, @@ -73425,311 +95513,328 @@ "typed_rows" ] }, - "DamagedMissingReportRow": { + "FabLabHealthRow": { "type": "object", "properties": { "makerspace_id": { - "type": "integer" + "type": "integer", + "description": "Tenant dimension included in both local and aggregate health reports." }, - "product": { - "type": "string" + "events_enabled": { + "type": "boolean" }, - "damaged_quantity": { - "type": "integer" + "events_available": { + "type": "boolean" }, - "missing_quantity": { - "type": "integer" - } - }, - "required": [ - "damaged_quantity", - "missing_quantity", - "product" - ] - }, - "Dashboard": { - "type": "object", - "properties": { - "upcoming_events": { + "events_in_period": { "type": "integer", - "default": 0 + "nullable": true }, - "pending_event_registrations": { + "events_registrations": { "type": "integer", - "default": 0 + "nullable": true }, - "pending_bookings": { + "events_attended": { "type": "integer", - "default": 0 + "nullable": true }, - "upcoming_bookings": { - "type": "integer", - "default": 0 + "events_completed_attendance_rate_percent": { + "type": "number", + "format": "double", + "nullable": true }, - "scope_mode": { - "$ref": "#/components/schemas/ScopeModeEnum" + "bookings_enabled": { + "type": "boolean" }, - "overdue_loans": { - "type": "integer", - "default": 0 + "bookings_available": { + "type": "boolean" }, - "pending_requests": { + "bookings_active_spaces": { "type": "integer", - "default": 0 + "nullable": true }, - "awaiting_issue": { + "bookings_non_cancelled": { "type": "integer", - "default": 0 + "nullable": true }, - "open_problem_reports": { - "type": "integer", - "default": 0 + "bookings_reserved_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", + "nullable": true }, - "low_stock": { + "bookings_upcoming": { "type": "integer", - "default": 0 + "nullable": true }, - "pending_prints": { + "bookings_no_shows": { "type": "integer", - "default": 0 + "nullable": true }, - "active_prints": { - "type": "integer", - "default": 0 + "bookings_reservation_utilization_percent": { + "type": "number", + "format": "double", + "nullable": true }, - "prints_awaiting_collection": { - "type": "integer", - "default": 0 + "machines_enabled": { + "type": "boolean" }, - "failed_emails": { - "type": "integer", - "default": 0 + "machines_available": { + "type": "boolean" }, - "stocktakes_awaiting_approval": { + "machines_active": { "type": "integer", - "default": 0 + "nullable": true }, - "warranty_expiring": { - "type": "integer", - "default": 0 + "machines_usage_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", + "nullable": true }, - "maintenance_overdue": { - "type": "integer", - "default": 0 + "maintenance_enabled": { + "type": "boolean" }, - "pending_payments": { + "maintenance_available": { + "type": "boolean" + }, + "maintenance_logs": { "type": "integer", - "default": 0 + "nullable": true }, - "outstanding_by_currency": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "maintenance_total_cost": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "maintenance_overdue_schedules": { + "type": "integer", + "nullable": true } }, "required": [ - "scope_mode" + "bookings_active_spaces", + "bookings_available", + "bookings_enabled", + "bookings_no_shows", + "bookings_non_cancelled", + "bookings_reservation_utilization_percent", + "bookings_reserved_hours", + "bookings_upcoming", + "events_attended", + "events_available", + "events_completed_attendance_rate_percent", + "events_enabled", + "events_in_period", + "events_registrations", + "machines_active", + "machines_available", + "machines_enabled", + "machines_usage_hours", + "maintenance_available", + "maintenance_enabled", + "maintenance_logs", + "maintenance_overdue_schedules", + "maintenance_total_cost", + "makerspace_id" ] }, - "DataExportCreate": { + "FactorRequiredEnum": { + "enum": [ + "totp", + "phone", + "email_code" + ], + "type": "string", + "description": "* `totp` - totp\n* `phone` - phone\n* `email_code` - email_code" + }, + "FailedDeliveries": { "type": "object", "properties": { - "fidelity": { - "allOf": [ - { - "$ref": "#/components/schemas/FidelityEnum" - } - ], - "default": "REDACTED" + "email": { + "type": "integer" + }, + "notifications": { + "type": "integer" } - } + }, + "required": [ + "email", + "notifications" + ] }, - "DataExportDownloadUrl": { + "FailureCodeEnum": { + "enum": [ + "deadline_exceeded", + "integrity_error", + "storage_error", + "quota_exceeded", + "internal_error" + ], + "type": "string", + "description": "* `` - None\n* `deadline_exceeded` - Deadline exceeded\n* `integrity_error` - Integrity error\n* `storage_error` - Storage error\n* `quota_exceeded` - Quota exceeded\n* `internal_error` - Internal error" + }, + "Feature4feEnum": { + "enum": [ + "hardware_requests", + "printing", + "events", + "bookings", + "maintenance", + "members", + "api_access", + "devices", + "payments", + "stock", + "certifications" + ], + "type": "string", + "description": "* `hardware_requests` - Hardware requests\n* `printing` - Printing\n* `events` - Events\n* `bookings` - Bookings\n* `maintenance` - Maintenance\n* `members` - Members\n* `api_access` - API access\n* `devices` - Devices\n* `payments` - Payments\n* `stock` - Stock and procurement\n* `certifications` - Certifications" + }, + "Feature9d8Enum": { + "enum": [ + "hardware_requests", + "printing", + "events", + "bookings", + "maintenance", + "members", + "api_access", + "devices", + "payments", + "stock", + "certifications" + ], + "type": "string", + "description": "* `hardware_requests` - hardware_requests\n* `printing` - printing\n* `events` - events\n* `bookings` - bookings\n* `maintenance` - maintenance\n* `members` - members\n* `api_access` - api_access\n* `devices` - devices\n* `payments` - payments\n* `stock` - stock\n* `certifications` - certifications" + }, + "FeedIssued": { "type": "object", "properties": { - "url": { + "feed_url": { "type": "string", "format": "uri" }, - "expires_at": { - "type": "string", - "format": "date-time" + "token_hint": { + "type": "string" } }, "required": [ - "expires_at", - "url" + "feed_url", + "token_hint" ] }, - "DataExportJob": { + "FeedState": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid", - "readOnly": true + "enabled": { + "type": "boolean" }, - "fidelity": { + "token_hint": { "type": "string", + "nullable": true + } + }, + "required": [ + "enabled", + "token_hint" + ] + }, + "FeedbackForm": { + "type": "object", + "properties": { + "event": { + "type": "object", + "additionalProperties": {}, "readOnly": true }, - "status": { + "survey": { "allOf": [ { - "$ref": "#/components/schemas/StatusE1dEnum" + "$ref": "#/components/schemas/FeedbackSurvey" } ], "readOnly": true }, - "manifest": { - "readOnly": true - }, - "failure_code": { - "readOnly": true, - "oneOf": [ - { - "$ref": "#/components/schemas/FailureCodeEnum" - }, + "mode": { + "allOf": [ { - "$ref": "#/components/schemas/BlankEnum" + "$ref": "#/components/schemas/FeedbackFormModeEnum" } - ] - }, - "failure_detail": { - "type": "string", + ], "readOnly": true }, - "deadline_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "snapshot_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "started_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "requires_auth": { + "type": "boolean", + "readOnly": true }, - "completed_at": { - "type": "string", - "format": "date-time", + "certificate": { + "allOf": [ + { + "$ref": "#/components/schemas/CertificateSummary" + } + ], "readOnly": true, "nullable": true - }, - "expires_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true } }, "required": [ - "completed_at", - "created_at", - "deadline_at", - "expires_at", - "failure_code", - "failure_detail", - "fidelity", - "id", - "manifest", - "snapshot_at", - "started_at", - "status" + "certificate", + "event", + "mode", + "requires_auth", + "survey" ] }, - "Delivery": { + "FeedbackFormModeEnum": { + "enum": [ + "anonymous", + "certificate" + ], + "type": "string", + "description": "* `anonymous` - anonymous\n* `certificate` - certificate" + }, + "FeedbackResponse": { "type": "object", "properties": { "id": { - "type": "integer" - }, - "kind": { - "$ref": "#/components/schemas/DeliveryKindEnum" - }, - "channel": { - "type": "string" - }, - "feature": { - "type": "string" - }, - "event": { - "type": "string" - }, - "destination_id": { "type": "integer", - "nullable": true - }, - "destination_label": { - "type": "string" - }, - "status": { - "type": "string" - }, - "error": { - "type": "string" - }, - "attempts": { - "type": "integer" + "readOnly": true }, - "retry_generation": { - "type": "integer" + "answers": { + "readOnly": true }, "created_at": { - "type": "string", - "format": "date-time" - }, - "sent_at": { "type": "string", "format": "date-time", - "nullable": true + "readOnly": true + }, + "identity": { + "type": "object", + "additionalProperties": {}, + "nullable": true, + "readOnly": true + }, + "certificate": { + "allOf": [ + { + "$ref": "#/components/schemas/CertificateSummary" + } + ], + "nullable": true, + "readOnly": true } }, "required": [ - "attempts", - "channel", + "answers", + "certificate", "created_at", - "destination_id", - "destination_label", - "error", - "event", - "feature", "id", - "kind", - "retry_generation", - "sent_at", - "status" + "identity" ] }, - "DeliveryEnum": { - "enum": [ - "web", - "device" - ], - "type": "string", - "description": "* `web` - Web\n* `device` - Device" - }, - "DeliveryKindEnum": { - "enum": [ - "email", - "channel" - ], - "type": "string", - "description": "* `email` - email\n* `channel` - channel" - }, - "DeliveryPage": { + "FeedbackResponseList": { "type": "object", "properties": { "count": { @@ -73748,7 +95853,7 @@ "results": { "type": "array", "items": { - "$ref": "#/components/schemas/Delivery" + "$ref": "#/components/schemas/FeedbackResponse" } } }, @@ -73759,397 +95864,518 @@ "results" ] }, - "DeliveryTest": { + "FeedbackSubmission": { "type": "object", "properties": { - "destination_id": { - "type": "integer", - "minimum": 1 + "answers": { + "type": "object", + "additionalProperties": {} + }, + "email": { + "type": "string", + "format": "email" } } }, - "DeliveryTestResult": { + "FeedbackSubmissionResponse": { "type": "object", "properties": { - "kind": { + "thank_you_text": { "type": "string" }, - "id": { - "type": "integer" - }, - "status": { - "type": "string" + "certificate": { + "allOf": [ + { + "$ref": "#/components/schemas/CertificateSummary" + } + ], + "nullable": true } }, "required": [ - "id", - "kind", - "status" + "certificate", + "thank_you_text" ] }, - "DeploymentIdentity": { + "FeedbackSurvey": { "type": "object", "properties": { - "algorithm": { - "type": "string" + "id": { + "type": "integer", + "readOnly": true }, - "deployment_id": { - "type": "string" + "title": { + "type": "string", + "readOnly": true }, - "public_key": { - "type": "string" + "thank_you_text": { + "type": "string", + "readOnly": true }, - "fingerprint": { - "type": "string" + "questions": { + "readOnly": true }, - "age_recipient": { - "type": "string" + "is_open": { + "type": "boolean", + "readOnly": true + }, + "certificate_enabled": { + "type": "boolean", + "readOnly": true + }, + "answered_question_ids": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "opened_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "closed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "response_count": { + "type": "integer", + "readOnly": true } }, "required": [ - "age_recipient", - "algorithm", - "deployment_id", - "fingerprint", - "public_key" + "answered_question_ids", + "certificate_enabled", + "closed_at", + "id", + "is_open", + "opened_at", + "questions", + "response_count", + "thank_you_text", + "title" ] }, - "DestinationDispatch": { + "FeedbackSurveyAdminEnvelope": { "type": "object", "properties": { - "detail": { - "type": "string" + "survey": { + "allOf": [ + { + "$ref": "#/components/schemas/FeedbackSurvey" + } + ], + "nullable": true } }, "required": [ - "detail" + "survey" ] }, - "DestinationProbe": { + "FeedbackSurveyWrite": { "type": "object", "properties": { - "ok": { - "type": "boolean" + "title": { + "type": "string", + "maxLength": 200 }, - "error_code": { - "type": "string" + "thank_you_text": { + "type": "string", + "default": "", + "maxLength": 2000 + }, + "questions": {}, + "certificate_enabled": { + "type": "boolean", + "default": false } }, "required": [ - "error_code", - "ok" + "questions", + "title" ] }, - "DestinationScope": { + "FeedbackTemplate": { "type": "object", "properties": { - "machine_type_ids": { + "template": { + "$ref": "#/components/schemas/TemplateEnum" + } + }, + "required": [ + "template" + ] + }, + "FidelityEnum": { + "enum": [ + "REDACTED" + ], + "type": "string", + "description": "* `REDACTED` - Readable — audit metadata and form answers redacted; member contact details included" + }, + "FieldEnum": { + "enum": [ + "name", + "resource", + "booking_window", + "reference" + ], + "type": "string", + "description": "* `name` - name\n* `resource` - resource\n* `booking_window` - booking_window\n* `reference` - reference" + }, + "FieldValidationError": { + "type": "object", + "description": "DRF's field-keyed boundary errors; deliberately not the typed service shape.", + "properties": { + "non_field_errors": { "type": "array", "items": { - "type": "integer" + "type": "string" } }, - "machine_ids": { + "digest": { "type": "array", "items": { - "type": "integer" + "type": "string" } }, - "category_ids": { + "decisions": { "type": "array", "items": { - "type": "integer" + "type": "string" } - } - } - }, - "Device": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "makerspace": { - "type": "integer", - "readOnly": true - }, - "kind": { - "allOf": [ - { - "$ref": "#/components/schemas/KindE45Enum" - } - ], - "readOnly": true }, - "name": { - "type": "string", - "readOnly": true + "approval_id": { + "type": "array", + "items": { + "type": "string" + } }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/DeviceStatusEnum" - } - ], - "readOnly": true + "target_age_recipient": { + "type": "array", + "items": { + "type": "string" + } }, - "public_key": { - "type": "string", - "readOnly": true, - "nullable": true + "archive": { + "type": "array", + "items": { + "type": "string" + } }, - "key_fingerprint": { - "type": "string", - "readOnly": true, - "nullable": true + "source_archive_digest": { + "type": "array", + "items": { + "type": "string" + } }, - "key_version": { - "type": "integer", - "readOnly": true + "target_identity": { + "type": "array", + "items": { + "type": "string" + } }, - "last_seen_at": { + "receipt": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "FirstStaffInvite": { + "type": "object", + "properties": { + "email": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "offline_threshold_seconds": { - "type": "integer", - "readOnly": true + "format": "email" }, - "offline_since": { + "role_slug": { + "$ref": "#/components/schemas/RoleSlugEnum" + } + }, + "required": [ + "email", + "role_slug" + ] + }, + "FontEnum": { + "enum": [ + "sans", + "serif" + ], + "type": "string", + "description": "* `sans` - sans\n* `serif` - serif" + }, + "ForgotPasswordRequest": { + "type": "object", + "properties": { + "email": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "format": "email" + } + }, + "required": [ + "email" + ] + }, + "FrontendDomainStatusEnum": { + "enum": [ + "pending", + "verified", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `verified` - Verified\n* `failed` - Failed" + }, + "GenericAnalyticsReport": { + "type": "object", + "properties": { + "report_key": { + "type": "string" }, - "offline_notified_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "created_by": { - "type": "integer", - "readOnly": true, - "nullable": true + "typed_rows": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + } }, - "created_at": { + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "rows" + ] + }, + "GenericObject": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + } + }, + "GithubBrowserStart": { + "type": "object", + "properties": { + "redirect_uri": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "uri", + "maxLength": 2048 }, - "revoked_at": { + "nonce": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "revoked_by": { - "type": "integer", - "readOnly": true, - "nullable": true + "writeOnly": true, + "pattern": "^[A-Za-z0-9_-]{43,128}$" } }, "required": [ - "created_at", - "created_by", - "id", - "key_fingerprint", - "key_version", - "kind", - "last_seen_at", - "makerspace", - "name", - "offline_notified_at", - "offline_since", - "offline_threshold_seconds", - "public_key", - "revoked_at", - "revoked_by", - "status" + "nonce", + "redirect_uri" ] }, - "DeviceChallengeResponse": { + "GithubBrowserStartResponse": { "type": "object", "properties": { - "challenge": { + "authorization_url": { + "type": "string", + "format": "uri" + }, + "state": { "type": "string" }, "expires_in": { - "type": "integer", - "minimum": 1 + "type": "integer" } }, "required": [ - "challenge", - "expires_in" + "authorization_url", + "expires_in", + "state" ] }, - "DeviceCreate": { + "GrantedBookingEntry": { "type": "object", "properties": { - "kind": { - "$ref": "#/components/schemas/KindE45Enum" + "space_id": { + "type": "integer" + }, + "makerspace_id": { + "type": "integer" + }, + "makerspace_slug": { + "type": "string" }, "name": { - "type": "string", - "maxLength": 120 + "type": "string" }, - "offline_threshold_seconds": { - "type": "integer", - "maximum": 604800, - "minimum": 1, - "default": 300 + "source": { + "$ref": "#/components/schemas/GrantedBookingEntrySourceEnum" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Permissions43eEnum" + } } }, "required": [ - "kind", - "name" + "makerspace_id", + "makerspace_slug", + "name", + "permissions", + "source", + "space_id" ] }, - "DeviceCreated": { + "GrantedBookingEntrySourceEnum": { + "enum": [ + "booking_grant" + ], + "type": "string", + "description": "* `booking_grant` - booking_grant" + }, + "GrantedEventEntry": { "type": "object", "properties": { - "device": { - "$ref": "#/components/schemas/Device" + "event_id": { + "type": "integer", + "nullable": true }, - "enrollment": { - "$ref": "#/components/schemas/EnrollmentGrant" + "series_id": { + "type": "integer", + "nullable": true + }, + "makerspace_id": { + "type": "integer" + }, + "makerspace_slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "source": { + "$ref": "#/components/schemas/GrantedEventEntrySourceEnum" + }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Permissions5d1Enum" + } } }, "required": [ - "device", - "enrollment" + "event_id", + "makerspace_id", + "makerspace_slug", + "permissions", + "series_id", + "source", + "title" ] }, - "DeviceError": { + "GrantedEventEntrySourceEnum": { + "enum": [ + "event_grant" + ], + "type": "string", + "description": "* `event_grant` - event_grant" + }, + "HardwareRequestError": { "type": "object", "properties": { "detail": { "type": "string" + }, + "code": { + "type": "string" } }, "required": [ + "code", "detail" ] }, - "DeviceGrant": { + "Health": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "platform": { - "type": "string" - }, - "app_id": { - "type": "string" - }, - "environment": { - "type": "string" - }, "status": { "type": "string" - }, - "attested_at": { - "type": "string", - "format": "date-time" - }, - "last_used_at": { - "type": "string", - "format": "date-time" - }, - "created_at": { - "type": "string", - "format": "date-time" } }, "required": [ - "app_id", - "attested_at", - "created_at", - "environment", - "id", - "last_used_at", - "platform", "status" ] }, - "DeviceIdentity": { + "HostWaiver": { "type": "object", "properties": { - "platform": { - "$ref": "#/components/schemas/PlatformEnum" + "id": { + "type": "integer", + "readOnly": true }, - "app_id": { + "version": { "type": "string", - "maxLength": 255, - "pattern": "^[A-Za-z0-9._-]{3,255}$" + "readOnly": true }, - "environment": { - "$ref": "#/components/schemas/EnvironmentEnum" + "body": { + "type": "string", + "readOnly": true } }, "required": [ - "app_id", - "environment", - "platform" + "body", + "id", + "version" ] }, - "DeviceLogin": { + "HostWaiverProjection": { "type": "object", "properties": { - "platform": { - "$ref": "#/components/schemas/PlatformEnum" - }, - "app_id": { - "type": "string", - "maxLength": 255, - "pattern": "^[A-Za-z0-9._-]{3,255}$" - }, - "environment": { - "$ref": "#/components/schemas/EnvironmentEnum" - }, - "username": { - "type": "string", - "maxLength": 254 + "id": { + "type": "integer", + "readOnly": true }, - "password": { + "version": { "type": "string", - "writeOnly": true, - "maxLength": 1024 + "readOnly": true }, - "challenge": { + "body": { "type": "string", - "maxLength": 512 - }, - "attestation": {} + "readOnly": true + } }, "required": [ - "app_id", - "attestation", - "challenge", - "environment", - "password", - "platform", - "username" + "body", + "id", + "version" ] }, - "DeviceLoginResult": { - "oneOf": [ - { - "$ref": "#/components/schemas/DeviceTokenResponse" - }, - { - "$ref": "#/components/schemas/MFAChallengeResponse" - } - ] + "HostWaiverStateEnum": { + "enum": [ + "not_required", + "on_file", + "missing" + ], + "type": "string", + "description": "* `not_required` - not_required\n* `on_file` - on_file\n* `missing` - missing" }, - "DeviceLogoutResponse": { + "HostingError": { "type": "object", "properties": { "detail": { @@ -74160,1085 +96386,1294 @@ "detail" ] }, - "DeviceRefresh": { + "IdentityDisclosureDecision": { "type": "object", "properties": { - "refresh": { - "type": "string", - "writeOnly": true, - "maxLength": 4096 + "user_id": { + "type": "integer" + }, + "approved": { + "type": "boolean" } }, "required": [ - "refresh" + "approved", + "user_id" ] }, - "DeviceRefreshResponse": { + "IdentityResolutionEnum": { + "enum": [ + "link_existing", + "create_walk_in" + ], + "type": "string", + "description": "* `link_existing` - Link existing account\n* `create_walk_in` - Create walk-in account" + }, + "ImportCreate": { "type": "object", "properties": { - "access": { - "type": "string" - }, - "refresh": { - "type": "string" + "archive": { + "type": "string", + "format": "uri", + "writeOnly": true }, - "device_grant": { - "$ref": "#/components/schemas/DeviceGrant" + "source_archive_digest": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" } }, "required": [ - "access", - "device_grant", - "refresh" + "archive", + "source_archive_digest" ] }, - "DeviceStatusEnum": { - "enum": [ - "pending", - "active", - "revoked" - ], - "type": "string", - "description": "* `pending` - Pending enrollment\n* `active` - Active\n* `revoked` - Revoked" + "ImportDecisionList": { + "type": "object", + "properties": { + "decisions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ImportIdentityDecision" + } + } + }, + "required": [ + "decisions" + ] }, - "DeviceTokenResponse": { + "ImportIdentityDecision": { "type": "object", "properties": { - "access": { - "type": "string" + "source_user_id": { + "type": "string", + "maxLength": 255 }, - "refresh": { - "type": "string" + "identity_resolution": { + "$ref": "#/components/schemas/IdentityResolutionEnum" }, - "user": { - "type": "object", - "additionalProperties": {} + "membership_disposition": { + "$ref": "#/components/schemas/MembershipDispositionEnum" }, - "device_grant": { - "$ref": "#/components/schemas/DeviceGrant" + "target_user_id": { + "type": "integer", + "nullable": true } }, "required": [ - "access", - "device_grant", - "refresh", - "user" + "identity_resolution", + "membership_disposition", + "source_user_id" ] }, - "DirectLoan": { + "ImportJob": { "type": "object", "properties": { - "has_kit_checklist": { - "type": "boolean", - "readOnly": true - }, - "public_token": { + "id": { "type": "string", "format": "uuid", "readOnly": true }, + "source_archive_digest": { + "type": "string", + "maxLength": 64 + }, + "source_makerspace_id": { + "type": "string", + "maxLength": 64 + }, + "source_makerspace_slug": { + "type": "string", + "maxLength": 100 + }, + "source_makerspace_name": { + "type": "string", + "maxLength": 200 + }, + "source_deployment_id": { + "type": "string", + "maxLength": 128 + }, + "storage_mode": { + "type": "string", + "maxLength": 32 + }, "status": { + "$ref": "#/components/schemas/ImportJobStatusEnum" + }, + "identity_count": { "type": "string", "readOnly": true }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicToolLoanItem" - }, + "target_lifecycle_state": { + "type": "string", + "nullable": true, "readOnly": true }, - "id": { - "type": "integer", - "readOnly": true + "source_deployment_identity": {}, + "aggregate_outcome": {}, + "failure_code": { + "type": "string", + "maxLength": 64 }, - "target_type": { + "failure_detail": { "type": "string", - "readOnly": true + "maxLength": 500 }, - "target_label": { + "created_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "container_id": { - "type": "integer", + "updated_at": { + "type": "string", + "format": "date-time", "readOnly": true }, - "container_label": { + "expires_at": { "type": "string", - "nullable": true, - "readOnly": true + "format": "date-time" }, - "due_at": { + "terminal_at": { "type": "string", "format": "date-time", - "readOnly": true, "nullable": true }, - "issue_evidence_id": { - "type": "integer", - "readOnly": true, + "scrubbed_at": { + "type": "string", + "format": "date-time", "nullable": true }, - "return_evidence_id": { + "source_retention_notice": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "created_at", + "expires_at", + "id", + "identity_count", + "source_archive_digest", + "source_retention_notice", + "target_lifecycle_state", + "updated_at" + ] + }, + "ImportJobStatusEnum": { + "enum": [ + "pending", + "awaiting_identity", + "ready", + "materializing", + "finalizing", + "completed", + "failed", + "abandoned" + ], + "type": "string", + "description": "* `pending` - Pending\n* `awaiting_identity` - Awaiting identity decisions\n* `ready` - Ready\n* `materializing` - Materializing\n* `finalizing` - Finalizing\n* `completed` - Completed\n* `failed` - Failed\n* `abandoned` - Abandoned" + }, + "ImportRun": { + "type": "object", + "properties": { + "target_identity": { + "type": "object", + "additionalProperties": {} + } + } + }, + "Inbox": { + "type": "object", + "properties": { + "id": { "type": "integer", - "readOnly": true, - "nullable": true + "readOnly": true }, - "return_notes": { + "feature": { "type": "string", "readOnly": true }, - "return_scan_required": { - "type": "boolean", + "event": { + "type": "string", "readOnly": true }, - "source": { + "title": { "type": "string", "readOnly": true }, - "issued_by": { - "allOf": [ - { - "$ref": "#/components/schemas/DirectLoanUserAttribution" - } - ], - "nullable": true, + "summary": { + "type": "string", "readOnly": true }, - "return_items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DirectLoanReturnItem" - }, + "link": { + "type": "string", + "readOnly": true + }, + "read_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", "readOnly": true } }, "required": [ - "container_id", - "container_label", - "due_at", - "has_kit_checklist", + "created_at", + "event", + "feature", "id", - "issue_evidence_id", - "issued_by", - "items", - "public_token", - "return_evidence_id", - "return_items", - "return_notes", - "return_scan_required", - "source", - "status", - "target_label", - "target_type" + "link", + "read_at", + "summary", + "title" ] }, - "DirectLoanIssue": { + "InboxPage": { "type": "object", "properties": { - "kit_checklist": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 - }, - "borrower_id": { - "type": "integer" - }, - "evidence_id": { + "count": { "type": "integer" }, - "remark": { - "type": "string" - }, - "container_id": { - "type": "integer", + "next": { + "type": "string", + "format": "uri", "nullable": true }, - "qr_payloads": { - "type": "array", - "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 50 + "previous": { + "type": "string", + "format": "uri", + "nullable": true }, - "items": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/DirectLoanItem" + "$ref": "#/components/schemas/Inbox" } } }, "required": [ - "borrower_id", - "evidence_id" + "count", + "next", + "previous", + "results" ] }, - "DirectLoanItem": { + "InheritFieldsEnum": { + "enum": [ + "cancellation_cutoff_hours", + "capacity", + "custom_form", + "description", + "ends_at", + "image_key", + "is_public", + "location", + "location_kind", + "payment_amount", + "registration_audience", + "registration_cutoff_at", + "registration_cutoff_lead_minutes", + "registration_requires_approval", + "starts_at", + "title" + ], + "type": "string", + "description": "* `cancellation_cutoff_hours` - cancellation_cutoff_hours\n* `capacity` - capacity\n* `custom_form` - custom_form\n* `description` - description\n* `ends_at` - ends_at\n* `image_key` - image_key\n* `is_public` - is_public\n* `location` - location\n* `location_kind` - location_kind\n* `payment_amount` - payment_amount\n* `registration_audience` - registration_audience\n* `registration_cutoff_at` - registration_cutoff_at\n* `registration_cutoff_lead_minutes` - registration_cutoff_lead_minutes\n* `registration_requires_approval` - registration_requires_approval\n* `starts_at` - starts_at\n* `title` - title" + }, + "Intake": { "type": "object", "properties": { - "product_id": { - "type": "integer" - }, - "quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1 - } + "intake_schema": {} }, "required": [ - "product_id", - "quantity" + "intake_schema" ] }, - "DirectLoanMember": { + "IntegrationBooking": { "type": "object", "properties": { - "membership_id": { - "type": "integer", - "readOnly": true + "id": { + "type": "integer" }, - "user_id": { - "type": "integer", - "readOnly": true + "resource_id": { + "type": "integer" }, - "display_name": { + "starts_at": { "type": "string", - "readOnly": true + "format": "date-time" }, - "username": { + "ends_at": { "type": "string", - "readOnly": true + "format": "date-time" }, - "is_walk_in": { - "type": "boolean", - "readOnly": true + "status": { + "type": "string" + }, + "display_name": { + "type": "string" + }, + "arrived_at": { + "type": "string", + "format": "date-time", + "nullable": true } }, "required": [ "display_name", - "is_walk_in", - "membership_id", - "user_id", - "username" + "ends_at", + "id", + "resource_id", + "starts_at", + "status" ] }, - "DirectLoanReturn": { + "IntegrationBookingPage": { "type": "object", "properties": { - "kit_checklist": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 - }, - "evidence_id": { - "type": "integer" - }, - "notes": { - "type": "string" - }, - "qr_payload": { + "next_cursor": { "type": "string", - "maxLength": 64 + "nullable": true }, - "resolutions": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/ReturnItemResolution" + "$ref": "#/components/schemas/IntegrationBooking" } } }, "required": [ - "evidence_id", - "notes" + "next_cursor", + "results" ] }, - "DirectLoanReturnAsset": { + "IntegrationBookingPublicPage": { "type": "object", "properties": { - "asset_id": { - "type": "integer" + "public_url": { + "type": "string", + "format": "uri" }, - "asset_tag": { + "embed_path": { + "type": "string" + }, + "payment_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "currency": { "type": "string" + }, + "rules": { + "$ref": "#/components/schemas/BookingPublicRules" + }, + "sections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntegrationBookingPublicSection" + } } }, "required": [ - "asset_id", - "asset_tag" + "currency", + "embed_path", + "payment_amount", + "public_url", + "rules", + "sections" ] }, - "DirectLoanReturnItem": { + "IntegrationBookingPublicSection": { "type": "object", "properties": { - "item_id": { - "type": "integer" + "kind": { + "type": "string" }, - "product_name": { + "slug": { "type": "string" }, - "remaining_quantity": { - "type": "integer" + "title": { + "type": "string" }, - "tracking_mode": { + "body": { "type": "string" }, - "assets": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DirectLoanReturnAsset" - } + "url": { + "type": "string" + }, + "position": { + "type": "integer" + }, + "image_url": { + "type": "string", + "nullable": true + }, + "image_alt_text": { + "type": "string" } }, "required": [ - "assets", - "item_id", - "product_name", - "remaining_quantity", - "tracking_mode" + "body", + "image_alt_text", + "image_url", + "kind", + "position", + "slug", + "title", + "url" ] }, - "DirectLoanUserAttribution": { + "IntegrationBookingResource": { "type": "object", "properties": { - "username": { + "id": { + "type": "integer" + }, + "name": { "type": "string" }, - "role": { + "kind": { + "type": "string" + }, + "description": { + "type": "string" + }, + "capacity": { + "type": "integer" + }, + "location": { "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "is_public": { + "type": "boolean" + }, + "public_page": { + "allOf": [ + { + "$ref": "#/components/schemas/IntegrationBookingPublicPage" + } + ], + "nullable": true, + "readOnly": true } }, "required": [ - "role", - "username" + "capacity", + "description", + "id", + "is_active", + "is_public", + "kind", + "location", + "name", + "public_page" ] }, - "Directory": { + "IntegrationBookingResourcePage": { "type": "object", "properties": { - "members": { + "next_cursor": { + "type": "string", + "nullable": true + }, + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/DirectoryEntry" + "$ref": "#/components/schemas/IntegrationBookingResource" } - }, - "hidden_count": { - "type": "integer" } }, "required": [ - "hidden_count", - "members" + "next_cursor", + "results" ] }, - "DirectoryEntry": { + "IntegrationConfiguredHealth": { "type": "object", - "description": "The listing row. Display name and avatar only -- never email, never phone.\n\nOtherwise \"see who else is here\" is an address harvest performed by anyone a space\nadmits, which is a different thing from what the member agreed to publish.", "properties": { - "membership_id": { - "type": "integer" + "status": { + "$ref": "#/components/schemas/Status83eEnum" }, - "display_name": { + "detail": { "type": "string" }, - "headline": { + "configured": { + "type": "boolean" + } + } + }, + "IntegrationDeliveriesByStream": { + "type": "object", + "properties": { + "status": { + "$ref": "#/components/schemas/Status83eEnum" + }, + "detail": { "type": "string" }, - "avatar_url": { + "hardware": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "printing": { "type": "string", + "format": "date-time", "nullable": true } - }, - "required": [ - "avatar_url", - "display_name", - "headline", - "membership_id" - ] - }, - "DispositionEnum": { - "enum": [ - "needs_fix", - "remove" - ], - "type": "string", - "description": "* `needs_fix` - needs_fix\n* `remove` - remove" + } }, - "DocTypeEnum": { - "enum": [ - "manual", - "sop" - ], - "type": "string", - "description": "* `manual` - Manual\n* `sop` - SOP" + "IntegrationEmailHealth": { + "type": "object", + "properties": { + "status": { + "$ref": "#/components/schemas/Status83eEnum" + }, + "detail": { + "type": "string" + }, + "total": { + "type": "integer" + }, + "pending": { + "type": "integer" + }, + "sent": { + "type": "integer" + }, + "failed": { + "type": "integer" + }, + "stalled": { + "type": "integer" + }, + "last_failure": { + "allOf": [ + { + "$ref": "#/components/schemas/IntegrationLastFailure" + } + ], + "nullable": true + } + } }, - "DocumentFinalize": { + "IntegrationEvent": { "type": "object", "properties": { - "object_key": { - "type": "string", - "maxLength": 255 + "id": { + "type": "integer" }, - "doc_type": { + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "starts_at": { "type": "string", - "maxLength": 16 + "format": "date-time" }, - "original_filename": { + "ends_at": { "type": "string", - "maxLength": 255 + "format": "date-time" + }, + "location": { + "type": "string" } }, "required": [ - "doc_type", - "object_key", - "original_filename" + "ends_at", + "id", + "location", + "starts_at", + "status", + "title" ] }, - "DocumentPage": { + "IntegrationEventPage": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "format": "uri", - "nullable": true - }, - "previous": { + "next_cursor": { "type": "string", - "format": "uri", "nullable": true }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/PaymentDocument" + "$ref": "#/components/schemas/IntegrationEvent" } } }, "required": [ - "count", - "next", - "previous", + "next_cursor", "results" ] }, - "DocumentPresign": { + "IntegrationHealth": { "type": "object", "properties": { - "filename": { - "type": "string", - "maxLength": 255 + "status": { + "$ref": "#/components/schemas/Status00eEnum" }, - "content_type": { - "type": "string", - "maxLength": 100 + "email": { + "$ref": "#/components/schemas/IntegrationEmailHealth" + }, + "deliveries_by_stream": { + "$ref": "#/components/schemas/IntegrationDeliveriesByStream" + }, + "smtp": { + "$ref": "#/components/schemas/IntegrationConfiguredHealth" + }, + "telegram": { + "$ref": "#/components/schemas/IntegrationConfiguredHealth" + }, + "worker": { + "$ref": "#/components/schemas/IntegrationWorkerHealth" + }, + "chat_destinations": { + "type": "object", + "additionalProperties": {} + }, + "sms": { + "$ref": "#/components/schemas/IntegrationConfiguredHealth" + }, + "native_push": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "content_type", - "filename" + "chat_destinations", + "deliveries_by_stream", + "email", + "native_push", + "sms", + "smtp", + "status", + "telegram", + "worker" ] }, - "DomainVerificationRecord": { + "IntegrationLastFailure": { "type": "object", "properties": { - "host": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "subject": { "type": "string" }, - "type": { + "error": { "type": "string" }, - "value": { + "stream": { "type": "string" } }, "required": [ - "host", - "type", - "value" + "created_at", + "error", + "stream", + "subject" ] }, - "DomainVerificationResponse": { + "IntegrationParticipant": { "type": "object", "properties": { - "status": { - "$ref": "#/components/schemas/StatusA39Enum" + "id": { + "type": "integer" }, - "token": { + "display_name": { "type": "string" }, - "expected_record": { - "allOf": [ - { - "$ref": "#/components/schemas/DomainVerificationRecord" - } - ], - "nullable": true + "registered_at": { + "type": "string", + "format": "date-time" }, - "verified_at": { + "attended_at": { "type": "string", "format": "date-time", "nullable": true - }, - "detail": { - "type": "string" } }, "required": [ - "detail", - "expected_record", - "status", - "token", - "verified_at" + "display_name", + "id", + "registered_at" ] }, - "DownloadGrant": { + "IntegrationParticipantPage": { "type": "object", "properties": { - "url": { - "type": "string" + "next_cursor": { + "type": "string", + "nullable": true }, - "expires_in": { - "type": "integer" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IntegrationParticipant" + } } }, "required": [ - "expires_in", - "url" + "next_cursor", + "results" ] }, - "EducationEntry": { + "IntegrationWorkerHealth": { "type": "object", "properties": { - "institution": { - "type": "string", - "maxLength": 200 + "status": { + "$ref": "#/components/schemas/Status83eEnum" }, - "qualification": { - "type": "string", - "default": "", - "maxLength": 200 + "detail": { + "type": "string" }, - "year": { + "broker_configured": { + "type": "boolean" + }, + "eager": { + "type": "boolean" + }, + "last_seen": { "type": "string", - "default": "", - "maxLength": 20 + "format": "date-time", + "nullable": true + }, + "stale": { + "type": "boolean" } - }, - "required": [ - "institution" - ] + } }, - "EmailLog": { + "IntegritySourceEnum": { + "enum": [ + "artifact_ledger", + "archive_row" + ], + "type": "string", + "description": "* `artifact_ledger` - Artifact ledger\n* `archive_row` - Archive row" + }, + "IntervalEnum": { + "enum": [ + "monthly", + "yearly", + "custom_days" + ], + "type": "string", + "description": "* `monthly` - Monthly\n* `yearly` - Yearly\n* `custom_days` - Custom number of days" + }, + "InventoryAssetAdmin": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "to_email": { - "type": "string", + "makerspace": { + "type": "integer", "readOnly": true }, - "subject": { - "type": "string", + "product": { + "type": "integer", "readOnly": true }, - "stream": { + "product_name": { "type": "string", "readOnly": true }, - "event": { + "box": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "box_label": { + "type": "string", + "readOnly": true, + "nullable": true + }, + "asset_tag": { "type": "string", "readOnly": true }, - "audience": { + "serial_number": { "type": "string", "readOnly": true }, "status": { "allOf": [ { - "$ref": "#/components/schemas/EmailLogStatusEnum" + "$ref": "#/components/schemas/InventoryAssetStatusEnum" } ], "readOnly": true }, - "error": { + "qr_code_id": { + "type": "integer", + "nullable": true, + "readOnly": true + }, + "qr_payload": { "type": "string", + "nullable": true, "readOnly": true }, - "attempts": { - "type": "integer", + "public_self_checkout_enabled": { + "type": "boolean", "readOnly": true }, - "created_at": { + "notes": { "type": "string", - "format": "date-time", "readOnly": true }, - "sent_at": { + "updated_at": { "type": "string", "format": "date-time", - "readOnly": true, - "nullable": true + "readOnly": true } }, "required": [ - "attempts", - "audience", - "created_at", - "error", - "event", + "asset_tag", + "box", + "box_label", "id", - "sent_at", + "makerspace", + "notes", + "product", + "product_name", + "public_self_checkout_enabled", + "qr_code_id", + "qr_payload", + "serial_number", "status", - "stream", - "subject", - "to_email" + "updated_at" ] }, - "EmailLogStatusEnum": { + "InventoryAssetStatusAction": { + "type": "object", + "properties": { + "action": { + "$ref": "#/components/schemas/InventoryAssetStatusActionActionEnum" + } + }, + "required": [ + "action" + ] + }, + "InventoryAssetStatusActionActionEnum": { "enum": [ - "pending", - "sending", - "sent", - "failed", - "skipped" + "shelve", + "repair" ], "type": "string", - "description": "* `pending` - Pending\n* `sending` - Sending\n* `sent` - Sent\n* `failed` - Failed\n* `skipped` - Skipped" + "description": "* `shelve` - shelve\n* `repair` - repair" }, - "EmailTemplateDetail": { + "InventoryAssetStatusEnum": { + "enum": [ + "available", + "reserved", + "issued", + "damaged", + "lost", + "retired", + "maintenance" + ], + "type": "string", + "description": "* `available` - Available\n* `reserved` - Reserved\n* `issued` - Issued\n* `damaged` - Damaged\n* `lost` - Lost\n* `retired` - Retired\n* `maintenance` - Maintenance" + }, + "InventoryChainOfCustodyResponse": { "type": "object", "properties": { - "stream": { - "type": "string", - "readOnly": true + "product_id": { + "type": "integer" }, - "audience": { - "type": "string", - "readOnly": true + "product_name": { + "type": "string" }, - "key": { - "type": "string", - "readOnly": true + "tracking_mode": { + "type": "string" }, - "label": { - "type": "string", - "readOnly": true + "limit": { + "type": "integer" }, - "description": { - "type": "string", - "readOnly": true + "truncated": { + "type": "boolean" }, - "fields": { + "events": { "type": "array", "items": { - "type": "object", - "additionalProperties": {} - }, - "readOnly": true + "$ref": "#/components/schemas/TimelineEvent" + } }, - "subject": { - "type": "string", - "readOnly": true + "asset_groups": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AssetChainGroup" + } }, - "text_body": { - "type": "string", + "quantity_summary": { + "allOf": [ + { + "$ref": "#/components/schemas/QuantityChainSummary" + } + ], + "nullable": true + } + }, + "required": [ + "asset_groups", + "events", + "limit", + "product_id", + "product_name", + "quantity_summary", + "tracking_mode", + "truncated" + ] + }, + "InventoryProductAdmin": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "html_body": { - "type": "string", + "makerspace": { + "type": "integer", "readOnly": true }, - "is_active": { - "type": "boolean", - "readOnly": true + "box": { + "type": "integer", + "nullable": true }, - "is_overridden": { - "type": "boolean", - "readOnly": true + "category": { + "type": "integer", + "nullable": true }, - "default_subject": { + "name": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "default_text": { + "manufacturer": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "default_html": { - "type": "string", - "readOnly": true - } - }, - "required": [ - "audience", - "default_html", - "default_subject", - "default_text", - "description", - "fields", - "html_body", - "is_active", - "is_overridden", - "key", - "label", - "stream", - "subject", - "text_body" - ] - }, - "EmailTemplateListItem": { - "type": "object", - "properties": { - "stream": { + "model": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "audience": { + "sku": { "type": "string", - "readOnly": true + "maxLength": 100 }, - "key": { + "barcode": { "type": "string", - "readOnly": true + "maxLength": 100 }, - "label": { + "supplier": { "type": "string", - "readOnly": true - }, - "is_active": { - "type": "boolean", - "readOnly": true + "maxLength": 200 }, - "is_overridden": { - "type": "boolean", - "readOnly": true + "reorder_point": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "can_edit_space_default": { - "type": "boolean", - "readOnly": true + "reorder_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "overridable_types": { + "kit_components": { "type": "array", "items": { - "$ref": "#/components/schemas/MachineTypeOption" + "$ref": "#/components/schemas/KitComponent" }, - "readOnly": true - } - }, - "required": [ - "audience", - "can_edit_space_default", - "is_active", - "is_overridden", - "key", - "label", - "overridable_types", - "stream" - ] - }, - "EmailTemplatePreviewRequest": { - "type": "object", - "properties": { - "stream": { - "type": "string" + "maxItems": 50 }, - "audience": { + "description": { "type": "string" }, - "key": { - "type": "string" + "unit_cost": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,4})?$", + "nullable": true }, - "machine_type_id": { - "type": "integer" + "cost_currency": { + "type": "string", + "maxLength": 3 }, - "subject": { + "cost_unit": { "type": "string", - "maxLength": 200 + "maxLength": 16 }, - "text_body": { - "type": "string" + "borrowing_terms": { + "type": "string", + "maxLength": 2000 }, - "html_body": { - "type": "string" - } - }, - "required": [ - "audience", - "html_body", - "key", - "stream", - "subject", - "text_body" - ] - }, - "EmailTemplatePreviewResponse": { - "type": "object", - "properties": { - "subject": { + "image_key": { "type": "string", "readOnly": true }, - "text_body": { + "image_url": { "type": "string", + "format": "uri", + "nullable": true, "readOnly": true }, - "html_body": { - "type": "string", + "tracking_mode": { + "$ref": "#/components/schemas/TrackingModeB86Enum" + }, + "loan_days": { + "type": "integer", + "maximum": 365, + "minimum": 1, + "nullable": true + }, + "max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "nullable": true + }, + "total_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "available_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "reserved_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "issued_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "damaged_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "lost_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "needs_fix_quantity": { + "type": "integer", "readOnly": true - } - }, - "required": [ - "html_body", - "subject", - "text_body" - ] - }, - "EmailVerificationConfirm": { - "type": "object", - "properties": { - "code": { - "type": "string", - "maxLength": 6 - } - }, - "required": [ - "code" - ] - }, - "EndReasonEnum": { - "enum": [ - "superseded", - "membership_revoked", - "claim_revoked", - "user_ended" - ], - "type": "string", - "description": "* `superseded` - Superseded\n* `membership_revoked` - Membership revoked\n* `claim_revoked` - Claim revoked\n* `user_ended` - User ended" - }, - "EnrollmentGrant": { - "type": "object", - "description": "Returned exactly once: the raw token is never stored and cannot be re-read.", - "properties": { - "enrollment_id": { - "type": "string", - "format": "uuid" }, - "device_id": { + "is_public": { + "type": "boolean" + }, + "public_self_checkout_enabled": { + "type": "boolean" + }, + "show_public_count": { + "type": "boolean" + }, + "public_availability_mode": { + "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" + }, + "storage_location": { "type": "string", - "format": "uuid" + "maxLength": 200 }, - "purpose": { - "$ref": "#/components/schemas/PurposeEnum" + "is_archived": { + "type": "boolean" }, - "token": { - "type": "string" + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "expires_at": { + "updated_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "readOnly": true } }, - "required": [ - "device_id", - "enrollment_id", - "expires_at", - "purpose", - "token" - ] - }, - "EnvironmentEnum": { - "enum": [ - "development", - "production" - ], - "type": "string", - "description": "* `development` - Development\n* `production` - Production" - }, - "Error": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - } + "required": [ + "created_at", + "id", + "image_key", + "image_url", + "makerspace", + "name", + "needs_fix_quantity", + "updated_at" + ] }, - "EventAdmin": { + "InventoryProductAdminCreate": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "makerspace_id": { + "makerspace": { "type": "integer", "readOnly": true }, - "ticketing_features": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true - }, - "series_summary": { - "type": "object", - "nullable": true, - "properties": { - "id": { - "type": "integer" - }, - "public_token": { - "type": "string", - "format": "uuid" - }, - "title": { - "type": "string" - }, - "timezone": { - "type": "string" - } - }, - "readOnly": true - }, - "series_revision": { + "box": { "type": "integer", - "readOnly": true, "nullable": true }, - "series_override_fields": { - "readOnly": true + "category": { + "type": "integer", + "nullable": true }, - "title": { + "name": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "description": { + "manufacturer": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "starts_at": { + "model": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 200 }, - "ends_at": { + "sku": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 100 }, - "timezone_name": { + "barcode": { "type": "string", - "readOnly": true + "maxLength": 100 }, - "location": { + "supplier": { "type": "string", - "readOnly": true - }, - "location_kind": { - "allOf": [ - { - "$ref": "#/components/schemas/LocationKindEnum" - } - ], - "readOnly": true + "maxLength": 200 }, - "custom_form": { - "readOnly": true, - "nullable": true + "reorder_point": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "capacity": { + "reorder_quantity": { "type": "integer", - "readOnly": true + "maximum": 2147483647, + "minimum": 0 }, - "payment_amount": { + "kit_components": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitComponent" + }, + "maxItems": 50 + }, + "description": { + "type": "string" + }, + "unit_cost": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,4})?$", + "nullable": true }, - "registration_requires_approval": { - "type": "boolean", - "readOnly": true + "cost_currency": { + "type": "string", + "maxLength": 3 }, - "registration_audience": { - "allOf": [ - { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" - } - ], + "cost_unit": { + "type": "string", + "maxLength": 16 + }, + "borrowing_terms": { + "type": "string", + "maxLength": 2000 + }, + "image_key": { + "type": "string", "readOnly": true }, - "category_id": { - "type": "integer", + "image_url": { + "type": "string", + "format": "uri", "nullable": true, "readOnly": true }, - "tags": { - "readOnly": true + "tracking_mode": { + "$ref": "#/components/schemas/TrackingModeB86Enum" }, - "cancellation_cutoff_hours": { + "loan_days": { "type": "integer", - "readOnly": true, + "maximum": 365, + "minimum": 1, "nullable": true }, - "registration_cutoff_at": { - "type": "string", - "format": "date-time", - "readOnly": true, + "max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0, "nullable": true }, - "registration_cutoff_lead_minutes": { + "total_quantity": { "type": "integer", - "readOnly": true, - "nullable": true + "maximum": 2147483647, + "minimum": 0 }, - "effective_registration_cutoff_at": { - "type": "string", - "format": "date-time", - "nullable": true, - "readOnly": true + "available_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "registration_open": { - "type": "boolean", + "reserved_quantity": { + "type": "integer", "readOnly": true }, - "offline_checkin_enabled": { - "type": "boolean", + "issued_quantity": { + "type": "integer", "readOnly": true }, - "is_public": { - "type": "boolean", + "damaged_quantity": { + "type": "integer", "readOnly": true }, - "image_url": { - "type": "string", - "format": "uri", - "nullable": true, + "lost_quantity": { + "type": "integer", "readOnly": true }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusFbbEnum" - } - ], + "needs_fix_quantity": { + "type": "integer", "readOnly": true }, - "created_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "is_public": { + "type": "boolean" + }, + "public_self_checkout_enabled": { + "type": "boolean" + }, + "show_public_count": { + "type": "boolean" + }, + "public_availability_mode": { + "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" + }, + "storage_location": { + "type": "string", + "maxLength": 200 + }, + "is_archived": { + "type": "boolean" }, "created_at": { "type": "string", @@ -75249,543 +97684,290 @@ "type": "string", "format": "date-time", "readOnly": true - }, - "registration_counts": { - "allOf": [ - { - "$ref": "#/components/schemas/EventRegistrationCounts" - } - ], - "readOnly": true - }, - "organizers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventOrganizerSummary" - }, - "readOnly": true } }, "required": [ - "cancellation_cutoff_hours", - "capacity", - "category_id", "created_at", - "created_by_id", - "custom_form", - "description", - "effective_registration_cutoff_at", - "ends_at", + "damaged_quantity", "id", + "image_key", "image_url", - "is_public", - "location", - "location_kind", - "makerspace_id", - "offline_checkin_enabled", - "organizers", - "payment_amount", - "registration_audience", - "registration_counts", - "registration_cutoff_at", - "registration_cutoff_lead_minutes", - "registration_open", - "registration_requires_approval", - "series_override_fields", - "series_revision", - "series_summary", - "starts_at", - "status", - "tags", - "ticketing_features", - "timezone_name", - "title", + "issued_quantity", + "lost_quantity", + "makerspace", + "name", + "needs_fix_quantity", + "reserved_quantity", "updated_at" ] }, - "EventAttendanceMark": { - "type": "object", - "properties": { - "source": { - "allOf": [ - { - "$ref": "#/components/schemas/EventAttendanceMarkSourceEnum" - } - ], - "default": "online" - } - } - }, - "EventAttendanceMarkSourceEnum": { - "enum": [ - "online", - "qr" - ], - "type": "string", - "description": "* `online` - online\n* `qr` - qr" - }, - "EventAttendanceReport": { - "type": "object", - "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventAttendanceRow" - } - } - }, - "required": [ - "rows", - "typed_rows" - ] - }, - "EventAttendanceRow": { + "InventoryProductAdminUpdate": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" + "id": { + "type": "integer", + "readOnly": true }, - "event_id": { - "type": "integer" + "makerspace": { + "type": "integer", + "readOnly": true }, - "series_id": { + "box": { "type": "integer", "nullable": true }, - "series_title": { - "type": "string" - }, - "series_occurrence_key": { - "type": "string" - }, - "title": { - "type": "string" + "category": { + "type": "integer", + "nullable": true }, - "starts_at": { + "name": { "type": "string", - "format": "date-time" - }, - "status": { - "type": "string" - }, - "capacity": { - "type": "integer" - }, - "registrations": { - "type": "integer" - }, - "confirmed": { - "type": "integer" - }, - "pending_approval": { - "type": "integer" + "maxLength": 200 }, - "registered": { - "type": "integer" + "manufacturer": { + "type": "string", + "maxLength": 200 }, - "waitlisted": { - "type": "integer" + "model": { + "type": "string", + "maxLength": 200 }, - "rejected": { - "type": "integer" + "sku": { + "type": "string", + "maxLength": 100 }, - "cancelled": { - "type": "integer" + "barcode": { + "type": "string", + "maxLength": 100 }, - "attended": { - "type": "integer" + "supplier": { + "type": "string", + "maxLength": 200 }, - "attendance_rate_percent": { - "type": "number", - "format": "double", - "nullable": true + "reorder_point": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "organizers": { - "type": "string" - } - }, - "required": [ - "attendance_rate_percent", - "attended", - "cancelled", - "capacity", - "confirmed", - "event_id", - "organizers", - "pending_approval", - "registered", - "registrations", - "rejected", - "series_id", - "series_occurrence_key", - "series_title", - "starts_at", - "status", - "title", - "waitlisted" - ] - }, - "EventCheckInResolveRequest": { - "type": "object", - "properties": { - "checkin_token": { - "type": "string" - } - }, - "required": [ - "checkin_token" - ] - }, - "EventCheckInResolveResponse": { - "type": "object", - "properties": { - "registration_id": { - "type": "integer" + "reorder_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "name": { - "type": "string" + "kit_components": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitComponent" + }, + "maxItems": 50 }, - "status": { + "description": { "type": "string" }, - "payment_status": { + "unit_cost": { "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,4})?$", "nullable": true }, - "host_waiver_state": { - "$ref": "#/components/schemas/HostWaiverStateEnum" - }, - "event_status": { - "type": "string" - }, - "confirmable": { - "type": "boolean" - } - }, - "required": [ - "confirmable", - "event_status", - "host_waiver_state", - "name", - "payment_status", - "registration_id", - "status" - ] - }, - "EventCollaborationInbox": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "event_id": { - "type": "integer", - "readOnly": true + "cost_currency": { + "type": "string", + "maxLength": 3 }, - "event_title": { + "cost_unit": { "type": "string", - "readOnly": true + "maxLength": 16 }, - "starts_at": { + "borrowing_terms": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 2000 }, - "ends_at": { + "image_key": { "type": "string", - "format": "date-time", "readOnly": true }, - "host_name": { + "image_url": { "type": "string", + "format": "uri", + "nullable": true, "readOnly": true }, - "host_slug": { - "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "tracking_mode": { + "$ref": "#/components/schemas/TrackingModeB86Enum" + }, + "loan_days": { + "type": "integer", + "maximum": 365, + "minimum": 1, + "nullable": true + }, + "max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "nullable": true + }, + "total_quantity": { + "type": "integer", + "readOnly": true }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusB9dEnum" - } - ], + "available_quantity": { + "type": "integer", "readOnly": true }, - "created_at": { - "type": "string", - "format": "date-time", + "reserved_quantity": { + "type": "integer", "readOnly": true }, - "responded_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - } - }, - "required": [ - "created_at", - "ends_at", - "event_id", - "event_title", - "host_name", - "host_slug", - "id", - "responded_at", - "starts_at", - "status" - ] - }, - "EventCollaborationRespond": { - "type": "object", - "properties": { - "accept": { - "type": "boolean" - } - }, - "required": [ - "accept" - ] - }, - "EventCollaborator": { - "type": "object", - "properties": { - "id": { + "issued_quantity": { "type": "integer", "readOnly": true }, - "event_id": { + "damaged_quantity": { "type": "integer", "readOnly": true }, - "makerspace_id": { + "lost_quantity": { "type": "integer", "readOnly": true }, - "makerspace_name": { - "type": "string", + "needs_fix_quantity": { + "type": "integer", "readOnly": true }, - "makerspace_slug": { - "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "is_public": { + "type": "boolean" }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusB9dEnum" - } - ], - "readOnly": true + "public_self_checkout_enabled": { + "type": "boolean" }, - "invited_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "show_public_count": { + "type": "boolean" }, - "responded_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "public_availability_mode": { + "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" + }, + "storage_location": { + "type": "string", + "maxLength": 200 + }, + "is_archived": { + "type": "boolean" }, "created_at": { "type": "string", "format": "date-time", "readOnly": true }, - "responded_at": { + "updated_at": { "type": "string", "format": "date-time", - "readOnly": true, - "nullable": true + "readOnly": true } }, "required": [ + "available_quantity", "created_at", - "event_id", + "damaged_quantity", "id", - "invited_by_id", - "makerspace_id", - "makerspace_name", - "makerspace_slug", - "responded_at", - "responded_by_id", - "status" - ] - }, - "EventCollaboratorReplace": { - "type": "object", - "properties": { - "slugs": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" - } - } - }, - "required": [ - "slugs" + "image_key", + "image_url", + "issued_quantity", + "lost_quantity", + "makerspace", + "name", + "needs_fix_quantity", + "reserved_quantity", + "total_quantity", + "updated_at" ] }, - "EventDefaultRegistrationAudienceEnum": { - "enum": [ - "members", - "accounts", - "invite" - ], - "type": "string", - "description": "* `members` - Members\n* `accounts` - Authenticated accounts\n* `invite` - Invited accounts" - }, - "EventEligibleMember": { + "InventoryQuantityAdjustment": { "type": "object", - "description": "A picker row. Name and id only — a roster is not a contact export.", "properties": { - "member_id": { + "delta_available": { "type": "integer", - "minimum": 1 + "default": 0 }, - "display_name": { + "delta_damaged": { + "type": "integer", + "default": 0 + }, + "delta_lost": { + "type": "integer", + "default": 0 + }, + "reason": { "type": "string" } }, "required": [ - "display_name", - "member_id" + "reason" ] }, - "EventListResponse": { + "Invitation": { "type": "object", "properties": { - "count": { + "role_id": { "type": "integer" }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { + "invite_email": { "type": "string", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventAdmin" - } + "format": "email", + "maxLength": 254 } }, "required": [ - "count", - "results" + "invite_email", + "role_id" ] }, - "EventListWithDefaults": { + "InvitationClaimOutcome": { "type": "object", "properties": { - "count": { + "id": { "type": "integer" }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventAdmin" - } - }, - "default_registration_audience": { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + "outcome": { + "$ref": "#/components/schemas/InvitationClaimOutcomeOutcomeEnum" } }, "required": [ - "count", - "default_registration_audience", - "results" + "id", + "outcome" ] }, - "EventOrganizerList": { - "type": "object", - "properties": { - "organizers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventOrganizerSummary" - }, - "readOnly": true - } - }, - "required": [ - "organizers" - ] + "InvitationClaimOutcomeOutcomeEnum": { + "enum": [ + "active", + "pending_approval", + "awaiting_vouches", + "awaiting_payment" + ], + "type": "string", + "description": "* `active` - active\n* `pending_approval` - pending_approval\n* `awaiting_vouches` - awaiting_vouches\n* `awaiting_payment` - awaiting_payment" }, - "EventOrganizerReplace": { + "InvitationList": { "type": "object", "properties": { - "organization_ids": { + "invitations": { "type": "array", "items": { - "type": "integer", - "minimum": 1 - }, - "maxItems": 50 - } - }, - "required": [ - "organization_ids" - ] - }, - "EventOrganizerSummary": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "slug": { - "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "name": { - "type": "string", - "readOnly": true + "$ref": "#/components/schemas/ClaimableInvitation" + } } }, "required": [ - "id", - "name", - "slug" + "invitations" ] }, - "EventRegistrationAdmin": { + "InvitationRequest": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "event_id": { - "type": "integer", - "readOnly": true - }, "name": { "type": "string", "readOnly": true @@ -75798,265 +97980,123 @@ "type": "string", "readOnly": true }, - "custom_answers": { - "readOnly": true, - "nullable": true - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/EventRegistrationAdminStatusEnum" - } - ], - "readOnly": true - }, - "created_at": { + "message": { "type": "string", - "format": "date-time", "readOnly": true }, - "payment": { + "status": { "allOf": [ { - "$ref": "#/components/schemas/StaffPaymentSummary" + "$ref": "#/components/schemas/InvitationRequestStatusEnum" } ], - "nullable": true, "readOnly": true }, - "ticket_type_id": { + "handled_by": { "type": "integer", "readOnly": true, "nullable": true }, - "quoted_amount": { + "handled_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "format": "date-time", "readOnly": true, "nullable": true }, - "discount_amount": { + "created_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "format": "date-time", "readOnly": true } }, "required": [ "created_at", - "custom_answers", - "discount_amount", "email", - "event_id", + "handled_at", + "handled_by", "id", + "message", "name", - "payment", "phone", - "quoted_amount", - "status", - "ticket_type_id" + "status" ] }, - "EventRegistrationAdminStatusEnum": { - "enum": [ - "pending_approval", - "registered", - "waitlisted", - "rejected", - "cancelled", - "attended" - ], - "type": "string", - "description": "* `pending_approval` - Pending approval\n* `registered` - Registered\n* `waitlisted` - Waitlisted\n* `rejected` - Rejected\n* `cancelled` - Cancelled\n* `attended` - Attended" - }, - "EventRegistrationCounts": { + "InvitationRequestAck": { "type": "object", "properties": { - "pending_approval": { - "type": "integer", - "readOnly": true - }, - "registered": { - "type": "integer", - "readOnly": true - }, - "waitlisted": { - "type": "integer", - "readOnly": true - }, - "rejected": { - "type": "integer", - "readOnly": true - }, - "cancelled": { - "type": "integer", - "readOnly": true - }, - "attended": { - "type": "integer", - "readOnly": true + "detail": { + "type": "string" } }, "required": [ - "attended", - "cancelled", - "pending_approval", - "registered", - "rejected", - "waitlisted" + "detail" ] }, - "EventRegistrationListResponse": { + "InvitationRequestCreate": { "type": "object", "properties": { - "count": { - "type": "integer" + "name": { + "type": "string", + "maxLength": 200 }, - "next": { + "email": { "type": "string", - "nullable": true + "format": "email", + "maxLength": 254 }, - "previous": { + "phone": { "type": "string", - "nullable": true + "default": "", + "maxLength": 32 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EventRegistrationAdmin" - } + "message": { + "type": "string", + "default": "", + "maxLength": 1000 + }, + "website": { + "type": "string", + "writeOnly": true } }, "required": [ - "count", - "results" + "email", + "name" ] }, - "EventSeriesDetail": { + "InvitationRequestStatusEnum": { + "enum": [ + "pending", + "invited", + "declined" + ], + "type": "string", + "description": "* `pending` - Pending\n* `invited` - Invited\n* `declined` - Declined" + }, + "Invite": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "public_token": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "makerspace_id": { - "type": "integer", - "readOnly": true - }, - "title": { - "type": "string", - "readOnly": true - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusFbbEnum" - } - ], - "readOnly": true - }, - "recurrence_timezone": { - "type": "string", - "readOnly": true - }, - "dtstart_local_date": { - "type": "string", - "format": "date", - "readOnly": true - }, - "dtstart_local_time": { - "type": "string", - "format": "time", - "readOnly": true - }, - "recurrence_rule": { - "type": "string", - "readOnly": true - }, - "duration_minutes": { - "type": "integer", - "readOnly": true - }, - "revision": { - "type": "integer", - "readOnly": true - }, - "next_occurrence_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "future_occurrence_count": { - "type": "integer", - "readOnly": true - }, - "last_materialized_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "last_generation_error_code": { - "type": "string", - "readOnly": true - }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "description": { - "type": "string" - }, - "location": { - "type": "string", - "maxLength": 255 - }, - "location_kind": { - "$ref": "#/components/schemas/LocationKindEnum" - }, - "custom_form": { - "nullable": true - }, - "capacity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "payment_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "registration_requires_approval": { - "type": "boolean" - }, - "registration_audience": { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" - }, - "cancellation_cutoff_hours": { - "type": "integer", - "maximum": 8760, - "minimum": 0, - "nullable": true + "max_uses": { + "type": "integer", + "readOnly": true }, - "registration_cutoff_lead_minutes": { + "uses": { "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true + "readOnly": true }, - "is_public": { - "type": "boolean" + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true }, - "created_by_id": { - "type": "integer", + "revoked_at": { + "type": "string", + "format": "date-time", "readOnly": true, "nullable": true }, @@ -76065,35 +98105,51 @@ "format": "date-time", "readOnly": true }, - "image_url": { + "code": { "type": "string", - "format": "uri", - "readOnly": true + "readOnly": true, + "description": "Returned once on creation only." } }, "required": [ + "code", "created_at", - "created_by_id", - "dtstart_local_date", - "dtstart_local_time", - "duration_minutes", - "future_occurrence_count", + "expires_at", "id", - "image_url", - "last_generation_error_code", - "last_materialized_at", - "makerspace_id", - "next_occurrence_at", - "public_token", - "recurrence_rule", - "recurrence_timezone", - "revision", - "status", - "title", - "updated_at" + "max_uses", + "revoked_at", + "uses" ] }, - "EventSeriesListResponse": { + "InviteInput": { + "type": "object", + "properties": { + "email": { + "writeOnly": true, + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "max_uses": { + "type": "integer", + "minimum": 1, + "default": 1 + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "InviteList": { "type": "object", "properties": { "count": { @@ -76101,16 +98157,18 @@ }, "next": { "type": "string", + "format": "uri", "nullable": true }, "previous": { "type": "string", + "format": "uri", "nullable": true }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/EventSeriesSummary" + "$ref": "#/components/schemas/Invite" } } }, @@ -76121,1867 +98179,1939 @@ "results" ] }, - "EventSeriesMutationResponse": { + "IssueReject": { "type": "object", "properties": { - "series": { + "item_id": { + "type": "integer" + }, + "broken": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "disposition": { "allOf": [ { - "$ref": "#/components/schemas/EventSeriesDetail" + "$ref": "#/components/schemas/DispositionEnum" } ], - "readOnly": true - }, - "created_occurrence_ids": { + "default": "needs_fix" + } + }, + "required": [ + "item_id" + ] + }, + "IssueRequest": { + "type": "object", + "properties": { + "kit_checklist": { "type": "array", "items": { - "type": "integer" + "$ref": "#/components/schemas/KitCount" }, - "readOnly": true + "maxItems": 3550 }, - "removed_occurrence_ids": { + "evidence_id": { + "type": "integer" + }, + "remark": { + "type": "string", + "default": "" + }, + "asset_qr_payloads": { "type": "array", "items": { - "type": "integer" - }, - "readOnly": true + "type": "string" + } }, - "affected_count": { - "type": "integer", - "readOnly": true + "rejects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IssueReject" + } } }, "required": [ - "affected_count", - "created_occurrence_ids", - "removed_occurrence_ids", - "series" + "evidence_id" ] }, - "EventSeriesSummary": { + "Job": { "type": "object", "properties": { "id": { - "type": "integer", + "type": "string", + "format": "uuid", "readOnly": true }, - "public_token": { + "command": { "type": "string", "format": "uuid", "readOnly": true }, - "makerspace_id": { + "makerspace": { "type": "integer", "readOnly": true }, - "title": { + "device": { "type": "string", + "format": "uuid", "readOnly": true }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusFbbEnum" - } - ], - "readOnly": true + "machine": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "recurrence_timezone": { + "service_request": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "machine_name": { "type": "string", "readOnly": true }, - "dtstart_local_date": { + "service_request_name": { "type": "string", - "format": "date", "readOnly": true }, - "dtstart_local_time": { + "external_job_id": { "type": "string", - "format": "time", "readOnly": true }, - "recurrence_rule": { + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/JobStateEnum" + } + ], + "readOnly": true, + "default": "awaiting" + }, + "telemetry_state": { "type": "string", "readOnly": true }, - "duration_minutes": { + "progress_percent": { "type": "integer", + "readOnly": true, + "nullable": true + }, + "telemetry_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "terminal_operation_id": { + "type": "string", + "format": "uuid", + "readOnly": true, + "nullable": true + }, + "outcome": { + "allOf": [ + { + "$ref": "#/components/schemas/JobOutcomeEnum" + } + ], "readOnly": true }, - "revision": { + "actual_minutes": { "type": "integer", - "readOnly": true + "readOnly": true, + "nullable": true }, - "next_occurrence_at": { + "failure_reason": { "type": "string", - "format": "date-time", "readOnly": true }, - "future_occurrence_count": { + "percent_complete": { "type": "integer", - "readOnly": true + "readOnly": true, + "nullable": true }, - "last_materialized_at": { + "actual_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true, + "nullable": true + }, + "actual_quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true, + "nullable": true + }, + "reported_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "last_generation_error_code": { + "applied_at": { "type": "string", - "readOnly": true + "format": "date-time", + "readOnly": true, + "nullable": true }, - "updated_at": { + "processed_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { "type": "string", "format": "date-time", "readOnly": true + }, + "consumptions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/JobConsumption" + }, + "readOnly": true } }, "required": [ - "dtstart_local_date", - "dtstart_local_time", - "duration_minutes", - "future_occurrence_count", + "actual_grams", + "actual_minutes", + "actual_quantity", + "applied_at", + "command", + "consumptions", + "created_at", + "device", + "external_job_id", + "failure_reason", "id", - "last_generation_error_code", - "last_materialized_at", - "makerspace_id", - "next_occurrence_at", - "public_token", - "recurrence_rule", - "recurrence_timezone", - "revision", - "status", - "title", - "updated_at" + "machine", + "machine_name", + "makerspace", + "outcome", + "percent_complete", + "processed_by", + "progress_percent", + "reported_at", + "service_request", + "service_request_name", + "state", + "telemetry_at", + "telemetry_state", + "terminal_operation_id" ] }, - "EventSeriesWrite": { + "JobConsumption": { "type": "object", "properties": { - "registration_audience": { - "$ref": "#/components/schemas/EventSeriesWriteRegistrationAudienceEnum" - }, - "ticket_types": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TicketInput" - }, - "writeOnly": true - }, - "title": { - "type": "string", - "maxLength": 200 - }, - "description": { - "type": "string", - "default": "" - }, - "location": { - "type": "string", - "default": "", - "maxLength": 255 - }, - "location_kind": { - "allOf": [ - { - "$ref": "#/components/schemas/LocationKindEnum" - } - ], - "default": "other" - }, - "custom_form": { + "machine_consumable": { + "type": "integer", "nullable": true }, - "capacity": { + "machine_consumable_id_snapshot": { "type": "integer", + "maximum": 9223372036854775807, "minimum": 0, - "default": 0 + "format": "int64" }, - "payment_amount": { + "quantity": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "default": "0.00" - }, - "registration_requires_approval": { - "type": "boolean", - "default": false - }, - "cancellation_cutoff_hours": { - "type": "integer", - "maximum": 8760, - "minimum": 0, - "nullable": true + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + } + }, + "required": [ + "machine_consumable_id_snapshot", + "quantity" + ] + }, + "JobCreate": { + "type": "object", + "properties": { + "binding_id": { + "type": "string", + "format": "uuid" }, - "registration_cutoff_lead_minutes": { + "service_request_id": { "type": "integer", - "minimum": 0, - "nullable": true - }, - "is_public": { - "type": "boolean", - "default": false + "minimum": 1 }, - "recurrence_timezone": { + "external_job_id": { "type": "string", - "maxLength": 64 + "maxLength": 120 + } + }, + "required": [ + "binding_id", + "external_job_id", + "service_request_id" + ] + }, + "JobOutcomeEnum": { + "enum": [ + "completed", + "failed" + ], + "type": "string", + "description": "* `completed` - Completed\n* `failed` - Failed" + }, + "JobStateEnum": { + "enum": [ + "awaiting", + "reported", + "applied", + "needs_review", + "cancelled" + ], + "type": "string", + "description": "* `awaiting` - Awaiting\n* `reported` - Reported\n* `applied` - Applied\n* `needs_review` - Needs review\n* `cancelled` - Cancelled" + }, + "JoinModeEnum": { + "enum": [ + "open", + "approval", + "invite_only", + "vouch", + "closed" + ], + "type": "string", + "description": "* `open` - Open\n* `approval` - Approval\n* `invite_only` - Invite only\n* `vouch` - Vouch\n* `closed` - Closed" + }, + "JournalLine": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "dtstart_local_date": { + "date": { "type": "string", "format": "date" }, - "dtstart_local_time": { - "type": "string", - "format": "time" + "document_number": { + "type": "string" }, - "recurrence_rule": { - "type": "string", - "maxLength": 500 + "kind": { + "type": "string" }, - "duration_minutes": { - "type": "integer", - "minimum": 1 + "payment_id": { + "type": "integer" }, - "effective_from": { + "subject_type": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "account_code": { + "type": "string" + }, + "debit": { "type": "string", - "format": "date-time", - "writeOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "credit": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "settlement_method": { + "type": "string" } }, "required": [ - "dtstart_local_date", - "dtstart_local_time", - "duration_minutes", - "recurrence_rule", - "recurrence_timezone", - "title" + "account_code", + "credit", + "currency", + "date", + "debit", + "document_number", + "kind", + "payment_id", + "settlement_method", + "subject_type" ] }, - "EventSeriesWriteRegistrationAudienceEnum": { + "Key292Enum": { + "enum": [ + "in_app", + "email", + "telegram", + "slack", + "mattermost", + "discord", + "webhook", + "native_push", + "sms", + "whatsapp" + ], + "type": "string", + "description": "* `in_app` - Inbox\n* `email` - Email\n* `telegram` - Telegram\n* `slack` - Slack\n* `mattermost` - Mattermost\n* `discord` - Discord\n* `webhook` - Signed webhook\n* `native_push` - Native push\n* `sms` - SMS\n* `whatsapp` - WhatsApp" + }, + "Key4feEnum": { + "enum": [ + "hardware_requests", + "printing", + "events", + "bookings", + "maintenance", + "members", + "api_access", + "devices", + "payments", + "stock", + "certifications" + ], + "type": "string", + "description": "* `hardware_requests` - Hardware requests\n* `printing` - Printing\n* `events` - Events\n* `bookings` - Bookings\n* `maintenance` - Maintenance\n* `members` - Members\n* `api_access` - API access\n* `devices` - Devices\n* `payments` - Payments\n* `stock` - Stock and procurement\n* `certifications` - Certifications" + }, + "Kind21fEnum": { + "enum": [ + "intro", + "logo", + "speaker", + "track", + "room", + "sponsor", + "faq", + "resource", + "highlight" + ], + "type": "string", + "description": "* `intro` - Introduction\n* `logo` - Page logo\n* `speaker` - Speaker\n* `track` - Track\n* `room` - Room\n* `sponsor` - Sponsor\n* `faq` - FAQ\n* `resource` - Resource\n* `highlight` - Highlight" + }, + "Kind3bfEnum": { + "enum": [ + "dev_room", + "bench", + "meeting", + "other" + ], + "type": "string", + "description": "* `dev_room` - Development room\n* `bench` - Bench\n* `meeting` - Meeting room\n* `other` - Other" + }, + "KindAf4Enum": { + "enum": [ + "rules", + "amenity", + "faq", + "link", + "sponsor", + "gallery" + ], + "type": "string", + "description": "* `rules` - Rules\n* `amenity` - Amenity\n* `faq` - FAQ\n* `link` - Link\n* `sponsor` - Sponsor\n* `gallery` - Gallery" + }, + "KindB02Enum": { + "enum": [ + "rollback_in_place", + "disaster" + ], + "type": "string", + "description": "* `rollback_in_place` - Rollback in place\n* `disaster` - Disaster or cross-server" + }, + "KindE45Enum": { + "enum": [ + "printer", + "label_printer", + "generic" + ], + "type": "string", + "description": "* `printer` - Printer\n* `label_printer` - Label printer\n* `generic` - Generic" + }, + "KindE56Enum": { "enum": [ + "role", + "requester", "members", - "accounts", - "invite" + "user" ], "type": "string", - "description": "* `members` - members\n* `accounts` - accounts\n* `invite` - invite" + "description": "* `role` - Role\n* `requester` - Requester\n* `members` - All members\n* `user` - Named user" }, - "EventStaffRegistration": { + "KioskAssignBox": { "type": "object", - "description": "Staff registering an identified account under the event's audience rules.\n\n`member_id` only. Contact details are copied off the account by the registration\nservice, so a staffer cannot record an attendee under a name and email that belong\nto nobody — which is what makes the attendee list usable as an accountability record\nrather than free text.", "properties": { - "member_id": { - "type": "integer", - "minimum": 1 - }, - "invite_code": { - "type": "string", - "writeOnly": true, - "maxLength": 128 - }, - "ticket_type_id": { + "request_id": { "type": "integer", "minimum": 1 }, - "promo_code": { - "type": "string", - "writeOnly": true, - "maxLength": 128 - }, - "custom_answers": { - "nullable": true - }, - "phone": { - "type": "string", - "default": "", - "maxLength": 32 - }, - "email": { - "oneOf": [ - { - "type": "string", - "format": "email", - "default": "" - }, - { - "type": "string", - "maxLength": 0 - } - ] + "box_code": { + "type": "string" } }, "required": [ - "member_id" + "box_code", + "request_id" ] }, - "EventWrite": { + "KioskEvidencePrepare": { "type": "object", "properties": { - "registration_audience": { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" - }, - "category_id": { - "type": "integer", - "minimum": 1, - "nullable": true - }, - "tags": { - "type": "array", - "items": { - "type": "string", - "maxLength": 50 - }, - "maxItems": 20 - }, - "title": { - "type": "string", - "maxLength": 200 - }, - "description": { - "type": "string", - "default": "" - }, - "starts_at": { - "type": "string", - "format": "date-time" - }, - "ends_at": { - "type": "string", - "format": "date-time" - }, - "timezone_name": { - "type": "string", - "maxLength": 64 - }, - "location": { - "type": "string", - "default": "", - "maxLength": 255 - }, - "location_kind": { - "allOf": [ - { - "$ref": "#/components/schemas/LocationKindEnum" - } - ], - "default": "other" - }, - "custom_form": { - "nullable": true - }, - "capacity": { - "type": "integer", - "minimum": 0, - "default": 0 - }, - "payment_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "default": "0.00" - }, - "is_public": { - "type": "boolean", - "default": false - }, - "registration_requires_approval": { - "type": "boolean", - "default": false - }, - "cancellation_cutoff_hours": { - "type": "integer", - "maximum": 8760, - "minimum": 0, - "nullable": true, - "description": "Member cancellation closes this many hours before start; null means start." + "evidence_type": { + "$ref": "#/components/schemas/EvidenceUrlRequestEvidenceTypeEnum" }, - "registration_cutoff_at": { - "type": "string", - "format": "date-time", - "nullable": true + "content_type": { + "type": "string" }, - "registration_cutoff_lead_minutes": { + "size_bytes": { "type": "integer", "minimum": 0, "nullable": true - }, - "inherit_fields": { - "type": "array", - "items": { - "$ref": "#/components/schemas/InheritFieldsEnum" - }, - "writeOnly": true } }, "required": [ - "ends_at", - "starts_at", - "title" + "content_type", + "evidence_type" ] }, - "EvidenceGetResponse": { + "KioskOnlineCommand": { "type": "object", "properties": { - "url": { - "type": "string", - "format": "uri" + "version": { + "$ref": "#/components/schemas/VersionEnum" }, - "expires_in": { - "type": "integer" + "kind": { + "$ref": "#/components/schemas/KioskOnlineCommandKindEnum" + }, + "payload": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "expires_in", - "url" + "kind", + "payload", + "version" ] }, - "EvidenceRetention": { + "KioskOnlineCommandKindEnum": { + "enum": [ + "assign_box", + "direct_handout", + "direct_return", + "resolve_qr", + "reviewed_issue", + "reviewed_return" + ], + "type": "string", + "description": "* `assign_box` - assign_box\n* `direct_handout` - direct_handout\n* `direct_return` - direct_return\n* `resolve_qr` - resolve_qr\n* `reviewed_issue` - reviewed_issue\n* `reviewed_return` - reviewed_return" + }, + "KioskOnlineCommandResult": { "type": "object", - "description": "Retention coordination only: ``null`` on the event means no state row exists,\nwhich is NOT a claim that the bytes are available.", "properties": { - "state": { - "$ref": "#/components/schemas/EvidenceRetentionStateEnum" + "kind": { + "type": "string" }, - "object_expired_at": { - "type": "string", - "format": "date-time", - "nullable": true + "result": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "object_expired_at", - "state" + "kind", + "result" ] }, - "EvidenceRetentionPolicy": { + "KioskOperatorLease": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, - "platform_default_days": { - "type": "integer" - }, - "override_days": { - "type": "integer", - "nullable": true - }, - "effective_days": { - "type": "integer" + "lease_token": { + "type": "string" }, - "object_expiry_enabled": { - "type": "boolean" + "lease_id": { + "type": "string", + "format": "uuid" }, - "abandoned_upload_cleanup_enabled": { - "type": "boolean" + "session_id": { + "type": "string", + "format": "uuid" }, - "abandoned_upload_platform_default_days": { + "actor_id": { "type": "integer" }, - "abandoned_upload_override_days": { - "type": "integer", - "nullable": true + "expires_at": { + "type": "string", + "format": "date-time" }, - "abandoned_upload_effective_days": { - "type": "integer" + "allowed_actions": { + "type": "array", + "items": { + "type": "string" + } }, - "abandoned_upload_global_enabled": { - "type": "boolean" + "permitted_operations": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "abandoned_upload_cleanup_enabled", - "abandoned_upload_effective_days", - "abandoned_upload_global_enabled", - "abandoned_upload_override_days", - "abandoned_upload_platform_default_days", - "effective_days", - "makerspace_id", - "object_expiry_enabled", - "override_days", - "platform_default_days" + "actor_id", + "allowed_actions", + "expires_at", + "lease_id", + "lease_token", + "permitted_operations", + "session_id" ] }, - "EvidenceRetentionPreviewRequest": { - "type": "object", - "properties": { - "limit": { - "type": "integer", - "maximum": 1000, - "minimum": 1, - "default": 100 - } - } - }, - "EvidenceRetentionPreviewResponse": { + "KioskStation": { "type": "object", "properties": { - "as_of": { - "type": "string", - "format": "date-time" - }, - "policy_days": { + "id": { "type": "integer" }, - "cutoff": { - "type": "string", - "format": "date-time" - }, - "object_candidates": { - "type": "integer" + "name": { + "type": "string" }, - "candidate_bytes": { - "type": "integer" + "kind": { + "type": "string" }, - "has_more": { + "enabled": { "type": "boolean" }, - "abandoned_upload_cutoff": { + "public_token": { "type": "string", - "format": "date-time", + "format": "uuid", "nullable": true }, - "abandoned_upload_candidates": { - "type": "integer" - }, - "abandoned_upload_candidate_bytes": { - "type": "integer" + "version": { + "type": "integer", + "nullable": true }, - "abandoned_upload_has_more": { - "type": "boolean" + "station_url": { + "type": "string", + "nullable": true }, - "late_write_receipts": { - "type": "integer" + "rotated_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "late_write_receipts_due": { - "type": "integer" + "created_at": { + "type": "string", + "format": "date-time" } }, "required": [ - "abandoned_upload_candidate_bytes", - "abandoned_upload_candidates", - "abandoned_upload_cutoff", - "abandoned_upload_has_more", - "as_of", - "candidate_bytes", - "cutoff", - "has_more", - "late_write_receipts", - "late_write_receipts_due", - "object_candidates", - "policy_days" + "created_at", + "enabled", + "id", + "kind", + "name", + "public_token", + "rotated_at", + "station_url", + "version" ] }, - "EvidenceRetentionStateEnum": { - "enum": [ - "expiring", - "expired" - ], - "type": "string", - "description": "* `expiring` - expiring\n* `expired` - expired" - }, - "EvidenceType740Enum": { - "enum": [ - "issue", - "return" - ], - "type": "string", - "description": "* `issue` - issue\n* `return` - return" - }, - "EvidenceUrlRequest": { + "KioskStationBootstrap": { "type": "object", "properties": { - "evidence_type": { - "$ref": "#/components/schemas/EvidenceUrlRequestEvidenceTypeEnum" + "station": { + "$ref": "#/components/schemas/_StationRef" }, - "content_type": { - "type": "string" + "makerspace": { + "$ref": "#/components/schemas/_MakerspaceRef" }, - "size_bytes": { - "type": "integer", - "minimum": 0, - "nullable": true + "session_expires_at": { + "type": "string", + "format": "date-time" + }, + "permitted_operations": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "content_type", - "evidence_type" + "makerspace", + "permitted_operations", + "session_expires_at", + "station" ] }, - "EvidenceUrlRequestEvidenceTypeEnum": { + "KioskStationCreate": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 160 + }, + "kind": { + "$ref": "#/components/schemas/KioskStationCreateKindEnum" + } + }, + "required": [ + "kind", + "name" + ] + }, + "KioskStationCreateKindEnum": { "enum": [ - "issue", - "return" + "scanner", + "handover" ], "type": "string", - "description": "* `issue` - Issue\n* `return` - Return" + "description": "* `scanner` - Scanner\n* `handover` - Handover" }, - "EvidenceUrlResponse": { + "KioskStationPin": { "type": "object", "properties": { - "evidence_id": { - "type": "integer" - }, - "upload_url": { + "pin": { "type": "string", - "format": "uri" - }, - "fields": { - "type": "object", - "additionalProperties": {} - }, - "object_key": { - "type": "string" - }, - "method": { - "type": "string" + "writeOnly": true, + "pattern": "^\\d{8}$" }, - "headers": { - "type": "object", - "additionalProperties": {} + "makerspace_slug": { + "type": "string", + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" } }, "required": [ - "evidence_id", - "fields", - "object_key", - "upload_url" + "pin" ] }, - "FabLabHealthReport": { + "KioskStationReveal": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FabLabHealthRow" - } + "current_password": { + "type": "string", + "writeOnly": true + } + }, + "required": [ + "current_password" + ] + }, + "KioskStationRevealResponse": { + "type": "object", + "properties": { + "pin": { + "type": "string" + }, + "version": { + "type": "integer" } }, "required": [ - "rows", - "typed_rows" + "pin", + "version" ] }, - "FabLabHealthRow": { + "KioskStationSecret": { "type": "object", "properties": { - "makerspace_id": { + "id": { "type": "integer" }, - "events_enabled": { - "type": "boolean" + "name": { + "type": "string" }, - "events_available": { + "kind": { + "type": "string" + }, + "enabled": { "type": "boolean" }, - "events_in_period": { - "type": "integer", + "public_token": { + "type": "string", + "format": "uuid", "nullable": true }, - "events_registrations": { + "version": { "type": "integer", "nullable": true }, - "events_attended": { - "type": "integer", + "station_url": { + "type": "string", "nullable": true }, - "events_completed_attendance_rate_percent": { - "type": "number", - "format": "double", + "rotated_at": { + "type": "string", + "format": "date-time", "nullable": true }, - "bookings_enabled": { - "type": "boolean" - }, - "bookings_available": { - "type": "boolean" - }, - "bookings_active_spaces": { - "type": "integer", - "nullable": true + "created_at": { + "type": "string", + "format": "date-time" }, - "bookings_non_cancelled": { - "type": "integer", - "nullable": true + "pin": { + "type": "string" + } + }, + "required": [ + "created_at", + "enabled", + "id", + "kind", + "name", + "pin", + "public_token", + "rotated_at", + "station_url", + "version" + ] + }, + "KioskWorkLine": { + "type": "object", + "properties": { + "item_id": { + "type": "integer" }, - "bookings_reserved_hours": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", - "nullable": true + "label": { + "type": "string" }, - "bookings_upcoming": { - "type": "integer", - "nullable": true + "quantity": { + "type": "integer" }, - "bookings_no_shows": { - "type": "integer", - "nullable": true + "returned": { + "type": "integer" + } + }, + "required": [ + "item_id", + "label", + "quantity", + "returned" + ] + }, + "KioskWorkPackage": { + "type": "object", + "properties": { + "kind": { + "type": "string" }, - "bookings_reservation_utilization_percent": { - "type": "number", - "format": "double", - "nullable": true + "id": { + "type": "integer" }, - "machines_enabled": { - "type": "boolean" + "status": { + "type": "string" }, - "machines_available": { - "type": "boolean" + "display_label": { + "type": "string" }, - "machines_active": { - "type": "integer", - "nullable": true + "person_display": { + "type": "string" }, - "machines_usage_hours": { + "box": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", "nullable": true }, - "maintenance_enabled": { - "type": "boolean" + "lines": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KioskWorkLine" + } }, - "maintenance_available": { - "type": "boolean" + "kit_components": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitExpectation" + } }, - "maintenance_logs": { - "type": "integer", - "nullable": true + "return_resolutions": { + "type": "array", + "items": { + "type": "string" + } }, - "maintenance_total_cost": { + "expires_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", - "nullable": true + "format": "date-time" }, - "maintenance_overdue_schedules": { - "type": "integer", - "nullable": true + "command_age_limit_seconds": { + "type": "integer" + }, + "evidence_max_bytes": { + "type": "integer" } }, "required": [ - "bookings_active_spaces", - "bookings_available", - "bookings_enabled", - "bookings_no_shows", - "bookings_non_cancelled", - "bookings_reservation_utilization_percent", - "bookings_reserved_hours", - "bookings_upcoming", - "events_attended", - "events_available", - "events_completed_attendance_rate_percent", - "events_enabled", - "events_in_period", - "events_registrations", - "machines_active", - "machines_available", - "machines_enabled", - "machines_usage_hours", - "maintenance_available", - "maintenance_enabled", - "maintenance_logs", - "maintenance_overdue_schedules", - "maintenance_total_cost" + "box", + "command_age_limit_seconds", + "display_label", + "evidence_max_bytes", + "expires_at", + "id", + "kind", + "kit_components", + "lines", + "person_display", + "return_resolutions", + "status" ] }, - "FailedDeliveries": { + "KioskWorkPackageRequest": { "type": "object", "properties": { - "email": { - "type": "integer" + "kind": { + "$ref": "#/components/schemas/KioskWorkPackageRequestKindEnum" }, - "notifications": { - "type": "integer" + "id": { + "type": "integer", + "minimum": 1 } }, "required": [ - "email", - "notifications" + "id", + "kind" ] }, - "FailureCodeEnum": { + "KioskWorkPackageRequestKindEnum": { "enum": [ - "deadline_exceeded", - "integrity_error", - "storage_error", - "quota_exceeded", - "internal_error" + "reviewed_request", + "direct_loan", + "direct_handout" ], "type": "string", - "description": "* `` - None\n* `deadline_exceeded` - Deadline exceeded\n* `integrity_error` - Integrity error\n* `storage_error` - Storage error\n* `quota_exceeded` - Quota exceeded\n* `internal_error` - Internal error" + "description": "* `reviewed_request` - reviewed_request\n* `direct_loan` - direct_loan\n* `direct_handout` - direct_handout" }, - "Feature4feEnum": { - "enum": [ - "hardware_requests", - "printing", - "events", - "bookings", - "maintenance", - "members", - "api_access", - "devices", - "payments", - "stock", - "certifications" - ], - "type": "string", - "description": "* `hardware_requests` - Hardware requests\n* `printing` - Printing\n* `events` - Events\n* `bookings` - Bookings\n* `maintenance` - Maintenance\n* `members` - Members\n* `api_access` - API access\n* `devices` - Devices\n* `payments` - Payments\n* `stock` - Stock and procurement\n* `certifications` - Certifications" + "KitComponent": { + "type": "object", + "properties": { + "key": { + "type": "string", + "format": "uuid" + }, + "label": { + "type": "string", + "maxLength": 200 + }, + "quantity": { + "type": "integer", + "maximum": 999, + "minimum": 1 + } + }, + "required": [ + "label", + "quantity" + ] }, - "Feature9d8Enum": { - "enum": [ - "hardware_requests", - "printing", - "events", - "bookings", - "maintenance", - "members", - "api_access", - "devices", - "payments", - "stock", - "certifications" - ], - "type": "string", - "description": "* `hardware_requests` - hardware_requests\n* `printing` - printing\n* `events` - events\n* `bookings` - bookings\n* `maintenance` - maintenance\n* `members` - members\n* `api_access` - api_access\n* `devices` - devices\n* `payments` - payments\n* `stock` - stock\n* `certifications` - certifications" + "KitCount": { + "type": "object", + "properties": { + "key": { + "type": "string", + "format": "uuid" + }, + "present": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + } + }, + "required": [ + "key", + "present" + ] }, - "FeedIssued": { + "KitExpectation": { "type": "object", "properties": { - "feed_url": { + "key": { "type": "string", - "format": "uri" + "format": "uuid" }, - "token_hint": { + "label": { + "type": "string" + }, + "owner_label": { "type": "string" + }, + "quantity": { + "type": "integer" + }, + "issue_present": { + "type": "integer" } }, "required": [ - "feed_url", - "token_hint" + "key", + "label", + "owner_label", + "quantity" ] }, - "FeedState": { + "KitPreview": { "type": "object", "properties": { - "enabled": { - "type": "boolean" - }, - "token_hint": { - "type": "string", - "nullable": true + "components": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitExpectation" + } } }, "required": [ - "enabled", - "token_hint" + "components" ] }, - "FeedbackForm": { + "KitPreviewInput": { "type": "object", "properties": { - "event": { - "type": "object", - "additionalProperties": {}, - "readOnly": true - }, - "survey": { + "phase": { "allOf": [ { - "$ref": "#/components/schemas/FeedbackSurvey" + "$ref": "#/components/schemas/PhaseEnum" } ], - "readOnly": true + "default": "issue" }, - "mode": { - "allOf": [ - { - "$ref": "#/components/schemas/FeedbackFormModeEnum" - } - ], - "readOnly": true + "request_id": { + "type": "integer", + "minimum": 1 }, - "requires_auth": { - "type": "boolean", - "readOnly": true + "loan_id": { + "type": "integer", + "minimum": 1 }, - "certificate": { - "allOf": [ - { - "$ref": "#/components/schemas/CertificateSummary" - } - ], - "readOnly": true, + "box_code": { + "type": "string" + }, + "container_id": { + "type": "integer", + "minimum": 1, "nullable": true + }, + "qr_payloads": { + "type": "array", + "items": { + "type": "string", + "maxLength": 64 + }, + "maxItems": 50 + }, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DirectLoanItem" + } + }, + "rejects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IssueReject" + } + }, + "resolutions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReturnItemResolution" + } } - }, - "required": [ - "certificate", - "event", - "mode", - "requires_auth", - "survey" - ] - }, - "FeedbackFormModeEnum": { - "enum": [ - "anonymous", - "certificate" - ], - "type": "string", - "description": "* `anonymous` - anonymous\n* `certificate` - certificate" + } }, - "FeedbackResponse": { + "LabelCommand": { "type": "object", "properties": { "id": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "command_id": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "command_status": { + "type": "string", + "readOnly": true + }, + "device_id": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "batch_id": { "type": "integer", + "readOnly": true, + "nullable": true + }, + "request_id": { + "type": "string", + "format": "uuid", "readOnly": true }, - "answers": { + "batch_id_snapshot": { + "type": "integer", "readOnly": true }, - "created_at": { + "batch_title_snapshot": { "type": "string", - "format": "date-time", "readOnly": true }, - "identity": { - "type": "object", - "additionalProperties": {}, - "nullable": true, + "item_count_snapshot": { + "type": "integer", "readOnly": true }, - "certificate": { + "result": { "allOf": [ { - "$ref": "#/components/schemas/CertificateSummary" + "$ref": "#/components/schemas/ResultEnum" } ], - "nullable": true, "readOnly": true + }, + "result_code": { + "type": "string", + "readOnly": true + }, + "result_message": { + "type": "string", + "readOnly": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "requested_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "batch_id", + "batch_id_snapshot", + "batch_title_snapshot", + "command_id", + "command_status", + "completed_at", + "created_at", + "device_id", + "id", + "item_count_snapshot", + "request_id", + "requested_by_id", + "result", + "result_code", + "result_message" + ] + }, + "LabelCreate": { + "type": "object", + "properties": { + "device_id": { + "type": "string", + "format": "uuid" + }, + "request_id": { + "type": "string", + "format": "uuid" } }, "required": [ - "answers", - "certificate", - "created_at", - "id", - "identity" + "device_id", + "request_id" ] }, - "FeedbackResponseList": { + "LabelReceipt": { "type": "object", "properties": { - "count": { - "type": "integer" + "operation_id": { + "type": "string", + "format": "uuid" }, - "next": { + "label_command_id": { "type": "string", - "format": "uri", - "nullable": true + "format": "uuid" }, - "previous": { + "outcome": { + "$ref": "#/components/schemas/OutcomeA1aEnum" + }, + "result_code": { + "$ref": "#/components/schemas/ResultCodeEnum" + }, + "response_result": { + "$ref": "#/components/schemas/ResponseResultEnum" + }, + "response_command_status": { + "type": "string" + }, + "response_completed_at": { "type": "string", - "format": "uri", + "format": "date-time", "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/FeedbackResponse" - } } }, "required": [ - "count", - "next", - "previous", - "results" + "label_command_id", + "operation_id", + "outcome", + "response_command_status", + "response_completed_at", + "response_result", + "result_code" ] }, - "FeedbackSubmission": { + "LabelResult": { "type": "object", "properties": { - "answers": { - "type": "object", - "additionalProperties": {} - }, - "email": { + "operation_id": { "type": "string", - "format": "email" + "format": "uuid" + }, + "outcome": { + "$ref": "#/components/schemas/OutcomeA1aEnum" + }, + "result_code": { + "$ref": "#/components/schemas/ResultCodeEnum" } - } + }, + "required": [ + "operation_id", + "outcome", + "result_code" + ] }, - "FeedbackSubmissionResponse": { + "LeaseResponse": { "type": "object", "properties": { - "thank_you_text": { - "type": "string" - }, - "certificate": { + "command": { "allOf": [ { - "$ref": "#/components/schemas/CertificateSummary" + "$ref": "#/components/schemas/CommandLease" } ], "nullable": true } }, "required": [ - "certificate", - "thank_you_text" + "command" ] }, - "FeedbackSurvey": { + "LedgerLine": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "makerspace_id": { + "type": "integer" }, - "title": { + "currency": { + "type": "string" + }, + "collected_amount": { "type": "string", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "thank_you_text": { + "refunded_amount": { "type": "string", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "questions": { - "readOnly": true + "date": { + "type": "string", + "format": "date-time" }, - "is_open": { - "type": "boolean", - "readOnly": true + "row_kind": { + "type": "string" }, - "certificate_enabled": { - "type": "boolean", - "readOnly": true + "row_id": { + "type": "integer" }, - "answered_question_ids": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true + "payment_id": { + "type": "integer" }, - "opened_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "document_number": { + "type": "string" }, - "closed_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "subject_type": { + "type": "string" }, - "response_count": { + "payer_type": { + "type": "string" + }, + "payer_id": { "type": "integer", - "readOnly": true - } - }, - "required": [ - "answered_question_ids", - "certificate_enabled", - "closed_at", - "id", - "is_open", - "opened_at", - "questions", - "response_count", - "thank_you_text", - "title" - ] - }, - "FeedbackSurveyAdminEnvelope": { - "type": "object", - "properties": { - "survey": { - "allOf": [ - { - "$ref": "#/components/schemas/FeedbackSurvey" - } - ], "nullable": true + }, + "status": { + "type": "string" + }, + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "owed_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "method": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "effective": { + "type": "boolean" + }, + "refund_status": { + "type": "string" } }, "required": [ - "survey" + "amount", + "collected_amount", + "currency", + "date", + "document_number", + "effective", + "method", + "owed_amount", + "payer_id", + "payer_type", + "payment_id", + "reference", + "refund_status", + "refunded_amount", + "row_id", + "row_kind", + "status", + "subject_type" ] }, - "FeedbackSurveyWrite": { + "LedgerResponse": { "type": "object", "properties": { - "title": { + "count": { + "type": "integer" + }, + "next": { "type": "string", - "maxLength": 200 + "nullable": true }, - "thank_you_text": { + "previous": { "type": "string", - "default": "", - "maxLength": 2000 + "nullable": true }, - "questions": {}, - "certificate_enabled": { - "type": "boolean", - "default": false + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LedgerRow" + } } }, "required": [ - "questions", - "title" + "count", + "results" ] }, - "FidelityEnum": { - "enum": [ - "REDACTED" - ], - "type": "string", - "description": "* `REDACTED` - Readable — audit metadata and form answers redacted; member contact details included" - }, - "FieldValidationError": { + "LedgerRow": { "type": "object", - "description": "DRF's field-keyed boundary errors; deliberately not the typed service shape.", "properties": { - "non_field_errors": { - "type": "array", - "items": { - "type": "string" - } - }, - "digest": { - "type": "array", - "items": { - "type": "string" - } + "source": { + "$ref": "#/components/schemas/LedgerRowSourceEnum" }, - "decisions": { - "type": "array", - "items": { - "type": "string" - } + "item_name": { + "type": "string" }, - "approval_id": { - "type": "array", - "items": { - "type": "string" - } + "container": { + "type": "string", + "nullable": true }, - "target_age_recipient": { - "type": "array", - "items": { - "type": "string" - } + "holder": { + "type": "string" }, - "archive": { - "type": "array", - "items": { - "type": "string" - } + "quantity": { + "type": "integer" }, - "source_archive_digest": { + "units": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/LedgerUnit" } }, - "target_identity": { - "type": "array", - "items": { - "type": "string" - } + "target_label": { + "type": "string", + "nullable": true }, - "receipt": { - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "FirstStaffInvite": { - "type": "object", - "properties": { - "email": { + "since": { "type": "string", - "format": "email" + "format": "date-time", + "nullable": true }, - "role_slug": { - "$ref": "#/components/schemas/RoleSlugEnum" - } - }, - "required": [ - "email", - "role_slug" - ] - }, - "ForgotPasswordRequest": { - "type": "object", - "properties": { - "email": { + "due": { "type": "string", - "format": "email" + "format": "date-time", + "nullable": true + }, + "makerspace_id": { + "type": "integer" + }, + "reference_id": { + "type": "integer" + }, + "status": { + "type": "string" } }, "required": [ - "email" + "due", + "holder", + "item_name", + "makerspace_id", + "quantity", + "reference_id", + "since", + "source", + "status", + "units" ] }, - "FrontendDomainStatusEnum": { + "LedgerRowSourceEnum": { "enum": [ - "pending", - "verified", - "failed" + "request", + "self_checkout", + "direct_handout" ], "type": "string", - "description": "* `pending` - Pending\n* `verified` - Verified\n* `failed` - Failed" + "description": "* `request` - request\n* `self_checkout` - self_checkout\n* `direct_handout` - direct_handout" }, - "GenericAnalyticsReport": { + "LedgerUnit": { "type": "object", "properties": { - "report_key": { + "asset_tag": { "type": "string" }, - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": {} - } - }, - "meta": { - "type": "object", - "additionalProperties": {} + "serial_number": { + "type": "string" } }, "required": [ - "rows" + "asset_tag", + "serial_number" ] }, - "GenericObject": { + "LegacyResetPasswordConfirm": { "type": "object", "properties": { - "detail": { + "uid": { "type": "string" + }, + "token": { + "type": "string", + "writeOnly": true + }, + "new_password": { + "type": "string", + "writeOnly": true } - } + }, + "required": [ + "new_password", + "token", + "uid" + ] }, - "GithubBrowserStart": { + "LegacyRoleEnum": { + "enum": [ + "space_manager", + "inventory_manager", + "print_manager", + "machine_manager", + "event_manager", + "booking_manager", + "handover" + ], + "type": "string", + "description": "* `space_manager` - Space Manager\n* `inventory_manager` - Inventory Manager\n* `print_manager` - Print Manager\n* `machine_manager` - Machine Manager\n* `event_manager` - Event Manager\n* `booking_manager` - Booking Manager\n* `handover` - Handover" + }, + "LendingHistoryActor": { "type": "object", "properties": { - "redirect_uri": { - "type": "string", - "format": "uri", - "maxLength": 2048 + "username": { + "type": "string" }, - "nonce": { - "type": "string", - "writeOnly": true, - "pattern": "^[A-Za-z0-9_-]{43,128}$" + "role": { + "type": "string" } }, "required": [ - "nonce", - "redirect_uri" + "role", + "username" ] }, - "GithubBrowserStartResponse": { + "LendingHistoryEntry": { "type": "object", "properties": { - "authorization_url": { - "type": "string", - "format": "uri" + "id": { + "type": "integer" }, - "state": { + "username": { "type": "string" }, - "expires_in": { + "issued_at": { + "type": "string", + "format": "date-time" + }, + "quantity": { "type": "integer" + }, + "issued_by": { + "allOf": [ + { + "$ref": "#/components/schemas/LendingHistoryActor" + } + ], + "nullable": true + }, + "accepted_by": { + "allOf": [ + { + "$ref": "#/components/schemas/LendingHistoryActor" + } + ], + "nullable": true } }, "required": [ - "authorization_url", - "expires_in", - "state" + "accepted_by", + "id", + "issued_at", + "issued_by", + "quantity", + "username" ] }, - "HardwareRequestError": { + "LendingHistoryResponse": { "type": "object", "properties": { - "detail": { - "type": "string" + "product_id": { + "type": "integer" }, - "code": { - "type": "string" + "last_borrower": { + "allOf": [ + { + "$ref": "#/components/schemas/LendingHistoryEntry" + } + ], + "nullable": true + }, + "recent": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LendingHistoryEntry" + } } }, "required": [ - "code", - "detail" + "last_borrower", + "product_id", + "recent" ] }, - "Health": { - "type": "object", - "properties": { - "status": { - "type": "string" - } - }, - "required": [ - "status" - ] + "LevelEnum": { + "enum": [ + "info", + "warning", + "critical" + ], + "type": "string", + "description": "* `info` - Info\n* `warning` - Warning\n* `critical` - Critical" }, - "HostWaiver": { + "LinkMachineConsumable": { "type": "object", "properties": { - "id": { + "measurement": { + "$ref": "#/components/schemas/Measurement883Enum" + }, + "product_id": { "type": "integer", - "readOnly": true + "nullable": true }, - "version": { + "label": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "body": { + "remaining": { "type": "string", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "low_threshold": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "note": { + "type": "string", + "maxLength": 255 } }, "required": [ - "body", - "id", - "version" + "measurement" ] }, - "HostWaiverStateEnum": { + "LoanDepositModeEnum": { "enum": [ - "not_required", - "on_file", - "missing" + "none", + "fixed", + "per_product" ], "type": "string", - "description": "* `not_required` - not_required\n* `on_file` - on_file\n* `missing` - missing" - }, - "HostingError": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - }, - "required": [ - "detail" - ] + "description": "* `none` - No deposit\n* `fixed` - Fixed amount per loan\n* `per_product` - Sum of product deposits" }, - "IdentityDisclosureDecision": { + "LoanRenewal": { "type": "object", "properties": { - "user_id": { + "id": { "type": "integer" }, - "approved": { - "type": "boolean" + "return_due_at": { + "type": "string", + "format": "date-time" + }, + "renewal_count": { + "type": "integer" } }, "required": [ - "approved", - "user_id" + "id", + "renewal_count", + "return_due_at" ] }, - "IdentityResolutionEnum": { + "LoanRenewalModeEnum": { "enum": [ - "link_existing", - "create_walk_in" + "off", + "auto" ], "type": "string", - "description": "* `link_existing` - Link existing account\n* `create_walk_in` - Create walk-in account" + "description": "* `off` - Off\n* `auto` - Automatic within policy" }, - "ImportCreate": { + "LocationKindEnum": { + "enum": [ + "indoor", + "outdoor", + "other" + ], + "type": "string", + "description": "* `indoor` - Indoor\n* `outdoor` - Outdoor\n* `other` - Other" + }, + "LogError": { "type": "object", "properties": { - "archive": { + "severity": { "type": "string", - "format": "uri", - "writeOnly": true + "maxLength": 16 }, - "source_archive_digest": { - "type": "string", - "pattern": "^[0-9a-f]{64}$" + "message": { + "type": "string" } }, "required": [ - "archive", - "source_archive_digest" + "message", + "severity" ] }, - "ImportDecisionList": { + "LogMachineConsumption": { "type": "object", "properties": { - "decisions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ImportIdentityDecision" - } + "quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" } }, "required": [ - "decisions" + "quantity" ] }, - "ImportIdentityDecision": { + "LogUsage": { "type": "object", "properties": { - "source_user_id": { - "type": "string", - "maxLength": 255 - }, - "identity_resolution": { - "$ref": "#/components/schemas/IdentityResolutionEnum" - }, - "membership_disposition": { - "$ref": "#/components/schemas/MembershipDispositionEnum" - }, - "target_user_id": { + "member": { "type": "integer", + "minimum": 1, "nullable": true - } - }, - "required": [ - "identity_resolution", - "membership_disposition", - "source_user_id" - ] - }, - "ImportJob": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "source_archive_digest": { - "type": "string", - "maxLength": 64 - }, - "source_makerspace_id": { - "type": "string", - "maxLength": 64 - }, - "source_makerspace_slug": { - "type": "string", - "maxLength": 100 - }, - "source_makerspace_name": { - "type": "string", - "maxLength": 200 - }, - "source_deployment_id": { - "type": "string", - "maxLength": 128 - }, - "storage_mode": { - "type": "string", - "maxLength": 32 - }, - "status": { - "$ref": "#/components/schemas/ImportJobStatusEnum" - }, - "identity_count": { - "type": "string", - "readOnly": true - }, - "target_lifecycle_state": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "source_deployment_identity": {}, - "aggregate_outcome": {}, - "failure_code": { - "type": "string", - "maxLength": 64 - }, - "failure_detail": { - "type": "string", - "maxLength": 500 - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "expires_at": { - "type": "string", - "format": "date-time" }, - "terminal_at": { + "started_at": { "type": "string", "format": "date-time", "nullable": true }, - "scrubbed_at": { + "hours": { "type": "string", - "format": "date-time", - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$" }, - "source_retention_notice": { + "note": { "type": "string", - "readOnly": true + "maxLength": 255 } }, "required": [ - "created_at", - "expires_at", - "id", - "identity_count", - "source_archive_digest", - "source_retention_notice", - "target_lifecycle_state", - "updated_at" + "hours" ] }, - "ImportJobStatusEnum": { - "enum": [ - "pending", - "awaiting_identity", - "ready", - "materializing", - "finalizing", - "completed", - "failed", - "abandoned" - ], - "type": "string", - "description": "* `pending` - Pending\n* `awaiting_identity` - Awaiting identity decisions\n* `ready` - Ready\n* `materializing` - Materializing\n* `finalizing` - Finalizing\n* `completed` - Completed\n* `failed` - Failed\n* `abandoned` - Abandoned" - }, - "ImportRun": { - "type": "object", - "properties": { - "target_identity": { - "type": "object", - "additionalProperties": {} - } - } - }, - "Inbox": { + "LoginRequest": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "feature": { - "type": "string", - "readOnly": true - }, - "event": { - "type": "string", - "readOnly": true - }, - "title": { - "type": "string", - "readOnly": true - }, - "summary": { - "type": "string", - "readOnly": true - }, - "link": { - "type": "string", - "readOnly": true + "username": { + "type": "string" }, - "read_at": { + "password": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "writeOnly": true }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "surface": { + "$ref": "#/components/schemas/Surface817Enum" } }, "required": [ - "created_at", - "event", - "feature", - "id", - "link", - "read_at", - "summary", - "title" + "password", + "username" ] }, - "InboxPage": { + "LoginResponse": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "format": "uri", - "nullable": true + "user": { + "$ref": "#/components/schemas/AuthUserPayload" }, - "previous": { - "type": "string", - "format": "uri", - "nullable": true + "access": { + "type": "string" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Inbox" - } + "surface": { + "$ref": "#/components/schemas/LoginResponseSurfaceEnum" } }, "required": [ - "count", - "next", - "previous", - "results" + "access", + "surface", + "user" ] }, - "InheritFieldsEnum": { + "LoginResponseSurfaceEnum": { "enum": [ - "cancellation_cutoff_hours", - "capacity", - "custom_form", - "description", - "ends_at", - "image_key", - "is_public", - "location", - "location_kind", - "payment_amount", - "registration_audience", - "registration_cutoff_at", - "registration_cutoff_lead_minutes", - "registration_requires_approval", - "starts_at", - "title" + "member", + "staff", + "staff_api", + "verification_only" ], "type": "string", - "description": "* `cancellation_cutoff_hours` - cancellation_cutoff_hours\n* `capacity` - capacity\n* `custom_form` - custom_form\n* `description` - description\n* `ends_at` - ends_at\n* `image_key` - image_key\n* `is_public` - is_public\n* `location` - location\n* `location_kind` - location_kind\n* `payment_amount` - payment_amount\n* `registration_audience` - registration_audience\n* `registration_cutoff_at` - registration_cutoff_at\n* `registration_cutoff_lead_minutes` - registration_cutoff_lead_minutes\n* `registration_requires_approval` - registration_requires_approval\n* `starts_at` - starts_at\n* `title` - title" + "description": "* `member` - member\n* `staff` - staff\n* `staff_api` - staff_api\n* `verification_only` - verification_only" }, - "Intake": { + "LogoutAllResponse": { "type": "object", "properties": { - "intake_schema": {} + "web_sessions": { + "type": "integer", + "minimum": 0, + "description": "Revoked browser sessions, including legacy sessions." + }, + "devices": { + "type": "integer", + "minimum": 0 + } }, "required": [ - "intake_schema" + "devices", + "web_sessions" ] }, - "IntegrationConfiguredHealth": { + "LogoutResponse": { "type": "object", "properties": { - "status": { - "$ref": "#/components/schemas/Status83eEnum" - }, "detail": { "type": "string" - }, - "configured": { - "type": "boolean" } - } + }, + "required": [ + "detail" + ] }, - "IntegrationDeliveriesByStream": { + "MFAChallenge": { "type": "object", "properties": { - "status": { - "$ref": "#/components/schemas/Status83eEnum" - }, - "detail": { - "type": "string" - }, - "hardware": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "printing": { + "challenge": { "type": "string", - "format": "date-time", - "nullable": true + "writeOnly": true, + "maxLength": 200, + "minLength": 20 } - } + }, + "required": [ + "challenge" + ] }, - "IntegrationEmailHealth": { + "MFAChallengeResponse": { "type": "object", "properties": { - "status": { - "$ref": "#/components/schemas/Status83eEnum" - }, - "detail": { - "type": "string" - }, - "total": { - "type": "integer" - }, - "pending": { - "type": "integer" + "mfa_required": { + "type": "boolean" }, - "sent": { - "type": "integer" + "enrollment_required": { + "type": "boolean" }, - "failed": { - "type": "integer" + "challenge": { + "type": "string" }, - "stalled": { + "expires_in": { "type": "integer" - }, - "last_failure": { - "allOf": [ - { - "$ref": "#/components/schemas/IntegrationLastFailure" - } - ], - "nullable": true } - } + }, + "required": [ + "challenge", + "enrollment_required", + "expires_in", + "mfa_required" + ] }, - "IntegrationHealth": { + "MFACompleted": { "type": "object", "properties": { - "status": { - "$ref": "#/components/schemas/Status00eEnum" - }, - "email": { - "$ref": "#/components/schemas/IntegrationEmailHealth" - }, - "deliveries_by_stream": { - "$ref": "#/components/schemas/IntegrationDeliveriesByStream" + "user": { + "$ref": "#/components/schemas/AuthUserPayload" }, - "smtp": { - "$ref": "#/components/schemas/IntegrationConfiguredHealth" + "access": { + "type": "string" }, - "telegram": { - "$ref": "#/components/schemas/IntegrationConfiguredHealth" + "surface": { + "type": "string" }, - "worker": { - "$ref": "#/components/schemas/IntegrationWorkerHealth" + "refresh": { + "type": "string" }, - "chat_destinations": { + "device_grant": { "type": "object", "additionalProperties": {} }, - "sms": { - "$ref": "#/components/schemas/IntegrationConfiguredHealth" - }, - "native_push": { - "type": "object", - "additionalProperties": {} + "recovery_codes": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "chat_destinations", - "deliveries_by_stream", - "email", - "native_push", - "sms", - "smtp", - "status", - "telegram", - "worker" + "access", + "surface", + "user" ] }, - "IntegrationLastFailure": { + "MFAEnrollment": { "type": "object", "properties": { - "created_at": { - "type": "string", - "format": "date-time" - }, - "subject": { + "secret": { "type": "string" }, - "error": { + "provisioning_uri": { "type": "string" }, - "stream": { + "qr_data_url": { "type": "string" } }, "required": [ - "created_at", - "error", - "stream", - "subject" + "provisioning_uri", + "qr_data_url", + "secret" ] }, - "IntegrationWorkerHealth": { + "MFAEnrollmentStart": { "type": "object", "properties": { - "status": { - "$ref": "#/components/schemas/Status83eEnum" - }, - "detail": { - "type": "string" - }, - "broker_configured": { + "password": { + "type": "string", + "writeOnly": true, + "default": "", + "maxLength": 1024 + } + } + }, + "MFAPassword": { + "type": "object", + "properties": { + "password": { + "type": "string", + "writeOnly": true, + "maxLength": 1024 + } + }, + "required": [ + "password" + ] + }, + "MFARecovery": { + "type": "object", + "properties": { + "recovery_codes": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "recovery_codes" + ] + }, + "MFAStatus": { + "type": "object", + "properties": { + "available": { "type": "boolean" }, - "eager": { + "password_available": { "type": "boolean" }, - "last_seen": { - "type": "string", - "format": "date-time", - "nullable": true + "enabled": { + "type": "boolean" }, - "stale": { + "required": { "type": "boolean" } - } - }, - "IntegritySourceEnum": { - "enum": [ - "artifact_ledger", - "archive_row" - ], - "type": "string", - "description": "* `artifact_ledger` - Artifact ledger\n* `archive_row` - Archive row" + }, + "required": [ + "available", + "enabled", + "required" + ] }, - "IntervalEnum": { - "enum": [ - "monthly", - "yearly", - "custom_days" - ], - "type": "string", - "description": "* `monthly` - Monthly\n* `yearly` - Yearly\n* `custom_days` - Custom number of days" + "MFAVerify": { + "type": "object", + "properties": { + "challenge": { + "type": "string", + "writeOnly": true, + "maxLength": 200, + "minLength": 20 + }, + "code": { + "type": "string", + "writeOnly": true, + "maxLength": 64, + "minLength": 6 + } + }, + "required": [ + "challenge", + "code" + ] }, - "InventoryAssetAdmin": { + "Machine": { "type": "object", "properties": { "id": { @@ -77992,56 +100122,110 @@ "type": "integer", "readOnly": true }, - "product": { - "type": "integer", - "readOnly": true - }, - "product_name": { - "type": "string", + "machine_type": { + "allOf": [ + { + "$ref": "#/components/schemas/MachineType" + } + ], "readOnly": true }, - "box": { + "machine_type_id": { "type": "integer", - "readOnly": true, - "nullable": true + "writeOnly": true }, - "box_label": { + "name": { "type": "string", - "readOnly": true, - "nullable": true + "maxLength": 200 }, - "asset_tag": { + "location": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "serial_number": { - "type": "string", - "readOnly": true + "notes": { + "type": "string" }, "status": { "allOf": [ { - "$ref": "#/components/schemas/InventoryAssetStatusEnum" + "$ref": "#/components/schemas/MachineStatusEnum" } ], "readOnly": true }, - "qr_code_id": { - "type": "integer", - "nullable": true, + "firmware_version": { + "type": "string", + "maxLength": 100 + }, + "camera_feed_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "status_since": { + "type": "string", + "format": "date-time", "readOnly": true }, - "qr_payload": { + "public_fields": {}, + "type_payload": {}, + "image_url": { "type": "string", "nullable": true, "readOnly": true }, - "public_self_checkout_enabled": { + "warranty_status": { + "type": "string", + "readOnly": true + }, + "is_public": { "type": "boolean", "readOnly": true }, - "notes": { + "is_active": { + "type": "boolean", + "readOnly": true + }, + "usage_hours": { + "type": "number", + "format": "double", + "readOnly": true + }, + "can_operate": { + "type": "boolean", + "readOnly": true + }, + "can_edit": { + "type": "boolean", + "readOnly": true + }, + "can_delegate": { + "type": "boolean", + "readOnly": true + }, + "can_retire": { + "type": "boolean", + "readOnly": true + }, + "can_unretire": { + "type": "boolean", + "readOnly": true + }, + "can_manage": { + "type": "boolean", + "readOnly": true + }, + "created_at": { "type": "string", + "format": "date-time", "readOnly": true }, "updated_at": { @@ -78051,818 +100235,818 @@ } }, "required": [ - "asset_tag", - "box", - "box_label", + "can_delegate", + "can_edit", + "can_manage", + "can_operate", + "can_retire", + "can_unretire", + "created_at", "id", + "image_url", + "is_active", + "is_public", + "machine_type", + "machine_type_id", "makerspace", - "notes", - "product", - "product_name", - "public_self_checkout_enabled", - "qr_code_id", - "qr_payload", - "serial_number", + "name", "status", - "updated_at" - ] - }, - "InventoryAssetStatusAction": { - "type": "object", - "properties": { - "action": { - "$ref": "#/components/schemas/InventoryAssetStatusActionActionEnum" - } - }, - "required": [ - "action" + "status_since", + "updated_at", + "usage_hours", + "warranty_status" ] }, - "InventoryAssetStatusActionActionEnum": { - "enum": [ - "shelve", - "repair" - ], - "type": "string", - "description": "* `shelve` - shelve\n* `repair` - repair" - }, - "InventoryAssetStatusEnum": { - "enum": [ - "available", - "reserved", - "issued", - "damaged", - "lost", - "retired", - "maintenance" - ], - "type": "string", - "description": "* `available` - Available\n* `reserved` - Reserved\n* `issued` - Issued\n* `damaged` - Damaged\n* `lost` - Lost\n* `retired` - Retired\n* `maintenance` - Maintenance" - }, - "InventoryChainOfCustodyResponse": { + "MachineConsumable": { "type": "object", "properties": { - "product_id": { - "type": "integer" - }, - "product_name": { - "type": "string" - }, - "tracking_mode": { - "type": "string" - }, - "limit": { - "type": "integer" - }, - "truncated": { - "type": "boolean" - }, - "events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TimelineEvent" - } - }, - "asset_groups": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AssetChainGroup" - } + "id": { + "type": "integer", + "readOnly": true }, - "quantity_summary": { + "measurement": { "allOf": [ { - "$ref": "#/components/schemas/QuantityChainSummary" + "$ref": "#/components/schemas/Measurement883Enum" } ], - "nullable": true - } - }, - "required": [ - "asset_groups", - "events", - "limit", - "product_id", - "product_name", - "quantity_summary", - "tracking_mode", - "truncated" - ] - }, - "InventoryProductAdmin": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer", "readOnly": true }, - "box": { - "type": "integer", - "nullable": true - }, - "category": { + "product": { "type": "integer", + "readOnly": true, "nullable": true }, - "name": { + "product_name": { "type": "string", - "maxLength": 200 + "nullable": true, + "readOnly": true }, - "manufacturer": { - "type": "string", - "maxLength": 200 + "available": { + "type": "integer", + "nullable": true, + "readOnly": true }, - "model": { + "remaining": { "type": "string", - "maxLength": 200 + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "nullable": true, + "readOnly": true }, - "sku": { + "label": { "type": "string", - "maxLength": 100 + "readOnly": true }, - "barcode": { + "low_threshold": { "type": "string", - "maxLength": 100 + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true, + "nullable": true }, - "supplier": { + "note": { "type": "string", - "maxLength": 200 - }, - "reorder_point": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "readOnly": true }, - "reorder_quantity": { + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "available", + "created_at", + "id", + "label", + "low_threshold", + "measurement", + "note", + "product", + "product_name", + "remaining" + ] + }, + "MachineDocument": { + "type": "object", + "properties": { + "id": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "kit_components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitComponent" - }, - "maxItems": 50 - }, - "description": { - "type": "string" + "readOnly": true }, - "borrowing_terms": { - "type": "string", - "maxLength": 2000 + "doc_type": { + "allOf": [ + { + "$ref": "#/components/schemas/DocTypeEnum" + } + ], + "readOnly": true }, - "image_key": { + "original_filename": { "type": "string", "readOnly": true }, - "image_url": { + "content_type": { "type": "string", - "format": "uri", - "nullable": true, "readOnly": true }, - "tracking_mode": { - "$ref": "#/components/schemas/TrackingModeB86Enum" - }, - "loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1, - "nullable": true - }, - "max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0, - "nullable": true - }, - "total_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "available_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "reserved_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "issued_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "damaged_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "lost_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "needs_fix_quantity": { + "size_bytes": { "type": "integer", "readOnly": true }, - "is_public": { - "type": "boolean" - }, - "public_self_checkout_enabled": { - "type": "boolean" - }, - "show_public_count": { - "type": "boolean" - }, - "public_availability_mode": { - "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" - }, - "storage_location": { - "type": "string", - "maxLength": 200 - }, - "is_archived": { - "type": "boolean" - }, "created_at": { "type": "string", "format": "date-time", "readOnly": true - }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true } }, "required": [ + "content_type", "created_at", + "doc_type", "id", - "image_key", - "image_url", - "makerspace", - "name", - "needs_fix_quantity", - "updated_at" + "original_filename", + "size_bytes" ] }, - "InventoryProductAdminCreate": { + "MachineDowntimeReport": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "makerspace": { - "type": "integer", - "readOnly": true + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineDowntimeRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "MachineDowntimeRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "box": { - "type": "integer", + "period": { + "type": "string", + "format": "date", "nullable": true }, - "category": { + "machine_id": { "type": "integer", "nullable": true }, - "name": { - "type": "string", - "maxLength": 200 - }, - "manufacturer": { - "type": "string", - "maxLength": 200 - }, - "model": { - "type": "string", - "maxLength": 200 - }, - "sku": { - "type": "string", - "maxLength": 100 - }, - "barcode": { - "type": "string", - "maxLength": 100 - }, - "supplier": { + "machine_name": { "type": "string", - "maxLength": 200 - }, - "reorder_point": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "reorder_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "kit_components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitComponent" - }, - "maxItems": 50 - }, - "description": { - "type": "string" + "nullable": true }, - "borrowing_terms": { + "machine_type": { "type": "string", - "maxLength": 2000 + "nullable": true }, - "image_key": { + "status": { "type": "string", - "readOnly": true + "description": "Unknown before the first recorded status event." }, - "image_url": { + "status_hours": { "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true - }, - "tracking_mode": { - "$ref": "#/components/schemas/TrackingModeB86Enum" - }, - "loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1, - "nullable": true - }, - "max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0, + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true - }, - "total_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "available_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "reserved_quantity": { - "type": "integer", - "readOnly": true - }, - "issued_quantity": { - "type": "integer", - "readOnly": true - }, - "damaged_quantity": { + } + }, + "required": [ + "machine_id", + "machine_name", + "machine_type", + "period", + "status", + "status_hours" + ] + }, + "MachineErrorLog": { + "type": "object", + "properties": { + "id": { "type": "integer", "readOnly": true }, - "lost_quantity": { - "type": "integer", + "severity": { + "allOf": [ + { + "$ref": "#/components/schemas/MachineErrorLogSeverityEnum" + } + ], "readOnly": true }, - "needs_fix_quantity": { - "type": "integer", + "message": { + "type": "string", "readOnly": true }, - "is_public": { - "type": "boolean" - }, - "public_self_checkout_enabled": { - "type": "boolean" - }, - "show_public_count": { - "type": "boolean" - }, - "public_availability_mode": { - "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" - }, - "storage_location": { + "logged_by_username": { "type": "string", - "maxLength": 200 - }, - "is_archived": { - "type": "boolean" + "readOnly": true, + "nullable": true }, "created_at": { "type": "string", "format": "date-time", "readOnly": true - }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true } }, "required": [ "created_at", - "damaged_quantity", "id", - "image_key", - "image_url", - "issued_quantity", - "lost_quantity", - "makerspace", - "name", - "needs_fix_quantity", - "reserved_quantity", - "updated_at" + "logged_by_username", + "message", + "severity" ] }, - "InventoryProductAdminUpdate": { + "MachineErrorLogSeverityEnum": { + "enum": [ + "info", + "warning", + "error", + "critical" + ], + "type": "string", + "description": "* `info` - Info\n* `warning` - Warning\n* `error` - Error\n* `critical` - Critical" + }, + "MachineErrorsReport": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "makerspace": { - "type": "integer", - "readOnly": true + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineErrorsRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "MachineErrorsRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "box": { - "type": "integer", + "period": { + "type": "string", + "format": "date", "nullable": true }, - "category": { + "machine_id": { "type": "integer", "nullable": true }, - "name": { + "machine_name": { "type": "string", - "maxLength": 200 + "nullable": true }, - "manufacturer": { + "machine_type": { "type": "string", - "maxLength": 200 + "nullable": true }, - "model": { - "type": "string", - "maxLength": 200 + "severity": { + "$ref": "#/components/schemas/MachineErrorsRowSeverityEnum" }, - "sku": { - "type": "string", - "maxLength": 100 + "error_count": { + "type": "integer" + } + }, + "required": [ + "error_count", + "machine_id", + "machine_name", + "machine_type", + "period", + "severity" + ] + }, + "MachineErrorsRowSeverityEnum": { + "enum": [ + "info", + "warning", + "error", + "critical" + ], + "type": "string", + "description": "* `info` - info\n* `warning` - warning\n* `error` - error\n* `critical` - critical" + }, + "MachineHeatmapRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "barcode": { + "period": { "type": "string", - "maxLength": 100 + "format": "date" }, - "supplier": { - "type": "string", - "maxLength": 200 + "measure": { + "$ref": "#/components/schemas/MeasureEnum" }, - "reorder_point": { + "machine_type_id": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "nullable": true }, - "reorder_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "machine_type": { + "type": "string" }, - "kit_components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitComponent" - }, - "maxItems": 50 + "weekday": { + "type": "integer", + "maximum": 6, + "minimum": 0, + "description": "Monday is zero." }, - "description": { - "type": "string" + "hour": { + "type": "integer", + "maximum": 23, + "minimum": 0 }, - "borrowing_terms": { + "service_hours": { "type": "string", - "maxLength": 2000 + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true }, - "image_key": { + "manual_hours": { "type": "string", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true }, - "image_url": { + "booked_hours": { "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true - }, - "tracking_mode": { - "$ref": "#/components/schemas/TrackingModeB86Enum" - }, - "loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1, + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true + } + }, + "required": [ + "booked_hours", + "hour", + "machine_type", + "machine_type_id", + "manual_hours", + "measure", + "period", + "service_hours", + "weekday" + ] + }, + "MachineListResponse": { + "type": "object", + "properties": { + "count": { + "type": "integer" }, - "max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0, - "nullable": true + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Machine" + } + } + }, + "required": [ + "count", + "results" + ] + }, + "MachineMemberRow": { + "type": "object", + "properties": { + "period": { + "type": "string", + "format": "date" }, - "total_quantity": { + "member_id": { "type": "integer", - "readOnly": true + "nullable": true, + "description": "Null means unattributed, never inferred from contacts." }, - "available_quantity": { - "type": "integer", - "readOnly": true + "measure": { + "$ref": "#/components/schemas/MeasureEnum" }, - "reserved_quantity": { + "machine_type_id": { "type": "integer", - "readOnly": true + "nullable": true }, - "issued_quantity": { - "type": "integer", - "readOnly": true + "machine_type": { + "type": "string" }, - "damaged_quantity": { - "type": "integer", - "readOnly": true + "entries": { + "type": "integer" }, - "lost_quantity": { + "hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true + } + }, + "required": [ + "entries", + "hours", + "machine_type", + "machine_type_id", + "measure", + "member_id", + "period" + ] + }, + "MachineOperator": { + "type": "object", + "properties": { + "id": { "type": "integer", "readOnly": true }, - "needs_fix_quantity": { + "user": { "type": "integer", "readOnly": true }, - "is_public": { - "type": "boolean" - }, - "public_self_checkout_enabled": { - "type": "boolean" - }, - "show_public_count": { - "type": "boolean" - }, - "public_availability_mode": { - "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" - }, - "storage_location": { + "username": { "type": "string", - "maxLength": 200 + "readOnly": true }, - "is_archived": { - "type": "boolean" + "access_level": { + "allOf": [ + { + "$ref": "#/components/schemas/AccessLevelEnum" + } + ], + "readOnly": true }, - "created_at": { + "assigned_by_username": { "type": "string", - "format": "date-time", - "readOnly": true + "readOnly": true, + "nullable": true }, - "updated_at": { + "assigned_at": { "type": "string", "format": "date-time", "readOnly": true } }, "required": [ - "available_quantity", - "created_at", - "damaged_quantity", + "access_level", + "assigned_at", + "assigned_by_username", "id", - "image_key", - "image_url", - "issued_quantity", - "lost_quantity", - "makerspace", - "name", - "needs_fix_quantity", - "reserved_quantity", - "total_quantity", - "updated_at" + "user", + "username" ] }, - "InventoryQuantityAdjustment": { + "MachineServiceConsumption": { "type": "object", "properties": { - "delta_available": { - "type": "integer", - "default": 0 + "makerspace_id": { + "type": "integer" }, - "delta_damaged": { - "type": "integer", - "default": 0 + "machine_id": { + "type": "integer" }, - "delta_lost": { + "machine_name": { + "type": "string" + }, + "machine_type": { + "type": "string", + "nullable": true + }, + "measurement": { + "$ref": "#/components/schemas/MachineServiceConsumptionMeasurementEnum" + }, + "product_id": { "type": "integer", - "default": 0 + "nullable": true }, - "reason": { + "product_label": { "type": "string" + }, + "completed_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "failed_partial_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "total_used": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" } }, "required": [ - "reason" + "completed_amount", + "failed_partial_amount", + "machine_id", + "machine_name", + "machine_type", + "measurement", + "product_id", + "product_label", + "total_used" ] }, - "Invitation": { + "MachineServiceConsumptionMeasurementEnum": { + "enum": [ + "count", + "grams" + ], + "type": "string", + "description": "* `count` - count\n* `grams` - grams" + }, + "MachineServiceFailure": { "type": "object", "properties": { - "role_id": { + "makerspace_id": { "type": "integer" }, - "invite_email": { + "machine_id": { + "type": "integer" + }, + "machine_name": { + "type": "string" + }, + "machine_type": { "type": "string", - "format": "email", - "maxLength": 254 + "nullable": true + }, + "outcome": { + "type": "string" + }, + "failed_count": { + "type": "integer" + }, + "failed_partial_hours": { + "type": "number", + "format": "double" + }, + "failed_count_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "failed_grams_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" } }, "required": [ - "invite_email", - "role_id" + "failed_count", + "failed_count_amount", + "failed_grams_amount", + "failed_partial_hours", + "machine_id", + "machine_name", + "machine_type", + "outcome" ] }, - "InvitationClaimOutcome": { + "MachineServiceMachine": { "type": "object", "properties": { - "id": { + "makerspace_id": { "type": "integer" }, - "outcome": { - "$ref": "#/components/schemas/InvitationClaimOutcomeOutcomeEnum" + "machine_id": { + "type": "integer" + }, + "machine_name": { + "type": "string" + }, + "machine_type": { + "type": "string", + "nullable": true + }, + "request_count": { + "type": "integer" + }, + "completed_count": { + "type": "integer" + }, + "failed_count": { + "type": "integer" + }, + "completed_hours": { + "type": "number", + "format": "double" + }, + "failed_partial_hours": { + "type": "number", + "format": "double" + }, + "total_recorded_service_hours": { + "type": "number", + "format": "double" + }, + "failure_rate": { + "type": "number", + "format": "double", + "nullable": true } }, "required": [ - "id", - "outcome" + "completed_count", + "completed_hours", + "failed_count", + "failed_partial_hours", + "failure_rate", + "machine_id", + "machine_name", + "machine_type", + "request_count", + "total_recorded_service_hours" ] }, - "InvitationClaimOutcomeOutcomeEnum": { - "enum": [ - "active", - "pending_approval", - "awaiting_vouches", - "awaiting_payment" - ], - "type": "string", - "description": "* `active` - active\n* `pending_approval` - pending_approval\n* `awaiting_vouches` - awaiting_vouches\n* `awaiting_payment` - awaiting_payment" - }, - "InvitationList": { + "MachineServiceReport": { "type": "object", "properties": { - "invitations": { + "status_totals": { "type": "array", "items": { - "$ref": "#/components/schemas/ClaimableInvitation" + "$ref": "#/components/schemas/MachineServiceStatusTotals" + } + }, + "machines": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineServiceMachine" + } + }, + "consumption": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineServiceConsumption" + } + }, + "failure_summary": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineServiceFailure" } } }, "required": [ - "invitations" + "consumption", + "failure_summary", + "machines", + "status_totals" ] }, - "InvitationRequest": { + "MachineServiceReportResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/MachineServiceReport" + }, + { + "$ref": "#/components/schemas/PrinterServiceReport" + } + ] + }, + "MachineServiceRequest": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "name": { - "type": "string", + "bucket_id": { + "type": "integer", "readOnly": true }, - "email": { - "type": "string", + "queue_id": { + "type": "integer", "readOnly": true }, - "phone": { + "machine_type": { "type": "string", "readOnly": true }, - "message": { - "type": "string", + "machine": { + "allOf": [ + { + "$ref": "#/components/schemas/ServiceMachine" + } + ], "readOnly": true }, - "status": { + "assigned_machine": { "allOf": [ { - "$ref": "#/components/schemas/InvitationRequestStatusEnum" + "$ref": "#/components/schemas/ServiceMachine" } ], "readOnly": true }, - "handled_by": { - "type": "integer", - "readOnly": true, - "nullable": true + "requester": { + "allOf": [ + { + "$ref": "#/components/schemas/ServiceRequester" + } + ], + "readOnly": true }, - "handled_at": { + "requester_name": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "readOnly": true }, - "created_at": { + "contact_email": { "type": "string", - "format": "date-time", "readOnly": true - } - }, - "required": [ - "created_at", - "email", - "handled_at", - "handled_by", - "id", - "message", - "name", - "phone", - "status" - ] - }, - "InvitationRequestAck": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - }, - "required": [ - "detail" - ] - }, - "InvitationRequestCreate": { - "type": "object", - "properties": { - "name": { + }, + "contact_phone": { "type": "string", - "maxLength": 200 + "readOnly": true }, - "email": { + "title": { "type": "string", - "format": "email", - "maxLength": 254 + "readOnly": true }, - "phone": { + "description": { "type": "string", - "default": "", - "maxLength": 32 + "readOnly": true }, - "message": { + "source_link": { "type": "string", - "default": "", - "maxLength": 1000 + "format": "uri", + "readOnly": true }, - "website": { + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/MachineServiceRequestStatusEnum" + } + ], + "readOnly": true + }, + "reason": { "type": "string", - "writeOnly": true - } - }, - "required": [ - "email", - "name" - ] - }, - "InvitationRequestStatusEnum": { - "enum": [ - "pending", - "invited", - "declined" - ], - "type": "string", - "description": "* `pending` - Pending\n* `invited` - Invited\n* `declined` - Declined" - }, - "Invite": { - "type": "object", - "properties": { - "id": { + "readOnly": true + }, + "estimated_minutes": { "type": "integer", "readOnly": true }, - "max_uses": { + "actual_minutes": { "type": "integer", "readOnly": true }, - "uses": { + "fail_percent_complete": { "type": "integer", "readOnly": true }, - "expires_at": { + "accepted_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "revoked_at": { + "started_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "failed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "collected_at": { "type": "string", "format": "date-time", "readOnly": true, @@ -78873,1150 +101057,1176 @@ "format": "date-time", "readOnly": true }, - "code": { + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "capability_payload": { + "readOnly": true + }, + "metering_unit": { "type": "string", "readOnly": true, - "description": "Returned once on creation only." + "nullable": true + }, + "planned_quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true, + "nullable": true + }, + "reserved_quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true, + "nullable": true + }, + "actual_consumed_quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true, + "nullable": true + }, + "planned_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true + }, + "reserved_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true + }, + "actual_consumed_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true + }, + "payment": { + "allOf": [ + { + "$ref": "#/components/schemas/StaffPayment" + } + ], + "nullable": true, + "readOnly": true + }, + "run_machine_model": { + "type": "string", + "readOnly": true + }, + "files": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ServiceFile" + }, + "readOnly": true + }, + "consumptions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ServiceConsumption" + }, + "readOnly": true } }, "required": [ - "code", + "accepted_at", + "actual_consumed_grams", + "actual_consumed_quantity", + "actual_minutes", + "assigned_machine", + "bucket_id", + "capability_payload", + "collected_at", + "completed_at", + "consumptions", + "contact_email", + "contact_phone", "created_at", - "expires_at", + "description", + "estimated_minutes", + "fail_percent_complete", + "failed_at", + "files", "id", - "max_uses", - "revoked_at", - "uses" + "machine", + "machine_type", + "metering_unit", + "payment", + "planned_grams", + "planned_quantity", + "queue_id", + "reason", + "requester", + "requester_name", + "reserved_grams", + "reserved_quantity", + "run_machine_model", + "source_link", + "started_at", + "status", + "title", + "updated_at" ] }, - "InviteInput": { + "MachineServiceRequestStatusEnum": { + "enum": [ + "pending", + "accepted", + "in_progress", + "completed", + "collected", + "rejected", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `accepted` - Accepted\n* `in_progress` - In progress\n* `completed` - Completed\n* `collected` - Collected\n* `rejected` - Rejected\n* `failed` - Failed" + }, + "MachineServiceStatusTotals": { "type": "object", "properties": { - "email": { - "writeOnly": true, - "oneOf": [ - { - "type": "string", - "format": "email" - }, - { - "type": "string", - "maxLength": 0 - } - ] + "makerspace_id": { + "type": "integer" }, - "max_uses": { - "type": "integer", - "minimum": 1, - "default": 1 + "submitted": { + "type": "integer" }, - "expires_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - } - }, - "InviteList": { - "type": "object", - "properties": { - "count": { + "accepted": { "type": "integer" }, - "next": { - "type": "string", - "format": "uri", - "nullable": true + "in_progress": { + "type": "integer" }, - "previous": { - "type": "string", - "format": "uri", - "nullable": true + "completed": { + "type": "integer" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Invite" - } + "collected": { + "type": "integer" + }, + "rejected": { + "type": "integer" + }, + "failed": { + "type": "integer" } }, "required": [ - "count", - "next", - "previous", - "results" + "accepted", + "collected", + "completed", + "failed", + "in_progress", + "rejected", + "submitted" ] }, - "IssueReject": { + "MachineServiceSubmit": { "type": "object", "properties": { - "item_id": { - "type": "integer" + "requester_id": { + "type": "integer", + "minimum": 1 }, - "broken": { + "machine_id": { "type": "integer", - "minimum": 0, - "default": 0 + "minimum": 1 }, - "disposition": { - "allOf": [ + "title": { + "type": "string", + "maxLength": 200 + }, + "description": { + "type": "string" + }, + "source_link": { + "oneOf": [ { - "$ref": "#/components/schemas/DispositionEnum" + "type": "string", + "format": "uri" + }, + { + "type": "string", + "maxLength": 0 } - ], - "default": "needs_fix" - } + ] + }, + "requester_name": { + "type": "string" + }, + "contact_email": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "contact_phone": { + "type": "string", + "maxLength": 32 + }, + "capability_payload": {} }, "required": [ - "item_id" + "machine_id", + "requester_id", + "title" ] }, - "IssueRequest": { + "MachineServiceV2Report": { "type": "object", "properties": { - "kit_checklist": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 - }, - "evidence_id": { - "type": "integer" - }, - "remark": { - "type": "string", - "default": "" - }, - "asset_qr_payloads": { + "rows": { "type": "array", "items": { - "type": "string" + "type": "array", + "items": {} } }, - "rejects": { + "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/IssueReject" + "$ref": "#/components/schemas/MachineServiceV2Row" } } }, "required": [ - "evidence_id" + "rows", + "typed_rows" ] }, - "Job": { + "MachineServiceV2Row": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid", - "readOnly": true + "makerspace_id": { + "type": "integer" }, - "command": { + "period": { "type": "string", - "format": "uuid", - "readOnly": true + "format": "date", + "nullable": true }, - "makerspace": { + "machine_id": { "type": "integer", - "readOnly": true + "nullable": true }, - "device": { + "machine_name": { "type": "string", - "format": "uuid", - "readOnly": true + "nullable": true }, - "machine": { + "machine_type": { + "type": "string", + "nullable": true + }, + "row_kind": { + "$ref": "#/components/schemas/RowKindEnum" + }, + "submitted": { "type": "integer", - "readOnly": true, "nullable": true }, - "service_request": { + "accepted": { "type": "integer", - "readOnly": true, "nullable": true }, - "machine_name": { - "type": "string", - "readOnly": true + "in_progress": { + "type": "integer", + "nullable": true }, - "service_request_name": { - "type": "string", - "readOnly": true + "completed": { + "type": "integer", + "nullable": true }, - "external_job_id": { - "type": "string", - "readOnly": true + "collected": { + "type": "integer", + "nullable": true }, - "state": { - "allOf": [ - { - "$ref": "#/components/schemas/JobStateEnum" - } - ], - "readOnly": true, - "default": "awaiting" + "rejected": { + "type": "integer", + "nullable": true }, - "telemetry_state": { - "type": "string", - "readOnly": true + "failed": { + "type": "integer", + "nullable": true }, - "progress_percent": { + "request_count": { "type": "integer", - "readOnly": true, "nullable": true }, - "telemetry_at": { - "type": "string", - "format": "date-time", - "readOnly": true, + "completed_count": { + "type": "integer", "nullable": true }, - "terminal_operation_id": { + "failed_count": { + "type": "integer", + "nullable": true + }, + "completed_hours": { "type": "string", - "format": "uuid", - "readOnly": true, + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true }, - "outcome": { - "allOf": [ - { - "$ref": "#/components/schemas/JobOutcomeEnum" - } - ], - "readOnly": true + "failed_partial_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true }, - "actual_minutes": { - "type": "integer", - "readOnly": true, + "total_recorded_service_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true }, - "failure_reason": { + "failure_rate": { + "type": "number", + "format": "double", + "nullable": true, + "description": "Failed / (completed + failed) × 100 at terminal time." + }, + "measurement": { "type": "string", - "readOnly": true + "nullable": true }, - "percent_complete": { + "product_id": { "type": "integer", - "readOnly": true, "nullable": true }, - "actual_grams": { + "product_label": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true, "nullable": true }, - "actual_quantity": { + "completed_amount": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true, + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true }, - "reported_at": { + "failed_partial_amount": { "type": "string", - "format": "date-time", - "readOnly": true, + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true }, - "applied_at": { + "total_used": { "type": "string", - "format": "date-time", - "readOnly": true, + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true }, - "processed_by": { - "type": "integer", - "readOnly": true, + "outcome": { + "type": "string", "nullable": true }, - "created_at": { + "failed_count_amount": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true }, - "consumptions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/JobConsumption" - }, - "readOnly": true + "failed_grams_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true } }, "required": [ - "actual_grams", - "actual_minutes", - "actual_quantity", - "applied_at", - "command", - "consumptions", - "created_at", - "device", - "external_job_id", - "failure_reason", - "id", - "machine", + "accepted", + "collected", + "completed", + "completed_amount", + "completed_count", + "completed_hours", + "failed", + "failed_count", + "failed_count_amount", + "failed_grams_amount", + "failed_partial_amount", + "failed_partial_hours", + "failure_rate", + "in_progress", + "machine_id", "machine_name", - "makerspace", + "machine_type", + "measurement", "outcome", - "percent_complete", - "processed_by", - "progress_percent", - "reported_at", - "service_request", - "service_request_name", - "state", - "telemetry_at", - "telemetry_state", - "terminal_operation_id" + "period", + "product_id", + "product_label", + "rejected", + "request_count", + "row_kind", + "submitted", + "total_recorded_service_hours", + "total_used" ] }, - "JobConsumption": { + "MachineStatusEnum": { + "enum": [ + "idle", + "running", + "reserved", + "maintenance", + "offline" + ], + "type": "string", + "description": "* `idle` - Idle\n* `running` - Running\n* `reserved` - Reserved\n* `maintenance` - Maintenance\n* `offline` - Offline" + }, + "MachineType": { "type": "object", "properties": { - "machine_consumable": { + "id": { "type": "integer", - "nullable": true + "readOnly": true }, - "machine_consumable_id_snapshot": { - "type": "integer", - "maximum": 9223372036854775807, - "minimum": 0, - "format": "int64" + "slug": { + "type": "string", + "maxLength": 50, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "quantity": { + "name": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - } - }, - "required": [ - "machine_consumable_id_snapshot", - "quantity" - ] - }, - "JobCreate": { - "type": "object", - "properties": { - "binding_id": { + "maxLength": 200 + }, + "icon": { "type": "string", - "format": "uuid" + "maxLength": 50 }, - "service_request_id": { - "type": "integer", - "minimum": 1 + "is_builtin": { + "type": "boolean" }, - "external_job_id": { + "managing_action": { "type": "string", - "maxLength": 120 + "readOnly": true + }, + "capability_config": { + "readOnly": true + }, + "makerspace": { + "type": "integer", + "readOnly": true, + "nullable": true } }, "required": [ - "binding_id", - "external_job_id", - "service_request_id" + "capability_config", + "id", + "makerspace", + "managing_action", + "name", + "slug" ] }, - "JobOutcomeEnum": { - "enum": [ - "completed", - "failed" - ], - "type": "string", - "description": "* `completed` - Completed\n* `failed` - Failed" - }, - "JobStateEnum": { - "enum": [ - "awaiting", - "reported", - "applied", - "needs_review", - "cancelled" - ], - "type": "string", - "description": "* `awaiting` - Awaiting\n* `reported` - Reported\n* `applied` - Applied\n* `needs_review` - Needs review\n* `cancelled` - Cancelled" - }, - "JoinModeEnum": { - "enum": [ - "open", - "approval", - "invite_only", - "vouch", - "closed" - ], - "type": "string", - "description": "* `open` - Open\n* `approval` - Approval\n* `invite_only` - Invite only\n* `vouch` - Vouch\n* `closed` - Closed" - }, - "JournalLine": { + "MachineTypeAccess": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" + "id": { + "type": "integer", + "readOnly": true }, - "date": { + "slug": { "type": "string", - "format": "date" - }, - "document_number": { - "type": "string" - }, - "kind": { - "type": "string" - }, - "payment_id": { - "type": "integer" + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "subject_type": { - "type": "string" + "name": { + "type": "string", + "readOnly": true }, - "currency": { - "type": "string" + "icon": { + "type": "string", + "readOnly": true }, - "account_code": { - "type": "string" + "is_builtin": { + "type": "boolean", + "readOnly": true }, - "debit": { + "managing_action": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "readOnly": true }, - "credit": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "capability_config": { + "readOnly": true }, - "settlement_method": { - "type": "string" - } - }, - "required": [ - "account_code", - "credit", - "currency", - "date", - "debit", - "document_number", - "kind", - "payment_id", - "settlement_method", - "subject_type" - ] - }, - "Key4feEnum": { - "enum": [ - "hardware_requests", - "printing", - "events", - "bookings", - "maintenance", - "members", - "api_access", - "devices", - "payments", - "stock", - "certifications" - ], - "type": "string", - "description": "* `hardware_requests` - Hardware requests\n* `printing` - Printing\n* `events` - Events\n* `bookings` - Bookings\n* `maintenance` - Maintenance\n* `members` - Members\n* `api_access` - API access\n* `devices` - Devices\n* `payments` - Payments\n* `stock` - Stock and procurement\n* `certifications` - Certifications" - }, - "KeyB30Enum": { - "enum": [ - "in_app", - "email", - "telegram", - "slack", - "mattermost", - "discord", - "webhook", - "native_push" - ], - "type": "string", - "description": "* `in_app` - Inbox\n* `email` - Email\n* `telegram` - Telegram\n* `slack` - Slack\n* `mattermost` - Mattermost\n* `discord` - Discord\n* `webhook` - Signed webhook\n* `native_push` - Native push" - }, - "Kind3bfEnum": { - "enum": [ - "dev_room", - "bench", - "meeting", - "other" - ], - "type": "string", - "description": "* `dev_room` - Development room\n* `bench` - Bench\n* `meeting` - Meeting room\n* `other` - Other" - }, - "KindB02Enum": { - "enum": [ - "rollback_in_place", - "disaster" - ], - "type": "string", - "description": "* `rollback_in_place` - Rollback in place\n* `disaster` - Disaster or cross-server" - }, - "KindE45Enum": { - "enum": [ - "printer", - "label_printer", - "generic" - ], - "type": "string", - "description": "* `printer` - Printer\n* `label_printer` - Label printer\n* `generic` - Generic" + "makerspace": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "can_create_machine": { + "type": "boolean", + "readOnly": true + } + }, + "required": [ + "can_create_machine", + "capability_config", + "icon", + "id", + "is_builtin", + "makerspace", + "managing_action", + "name", + "slug" + ] }, - "KindE56Enum": { - "enum": [ - "role", - "requester", - "members", - "user" - ], - "type": "string", - "description": "* `role` - Role\n* `requester` - Requester\n* `members` - All members\n* `user` - Named user" + "MachineTypeCreate": { + "type": "object", + "properties": { + "slug": { + "type": "string", + "maxLength": 50, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "name": { + "type": "string", + "maxLength": 200 + }, + "icon": { + "type": "string", + "maxLength": 50 + }, + "capability_config": {} + }, + "required": [ + "name", + "slug" + ] }, - "KioskAssignBox": { + "MachineTypeOption": { "type": "object", "properties": { - "request_id": { + "id": { "type": "integer", - "minimum": 1 + "readOnly": true }, - "box_code": { - "type": "string" + "name": { + "type": "string", + "readOnly": true + }, + "is_active": { + "type": "boolean", + "readOnly": true + }, + "is_overridden": { + "type": "boolean", + "readOnly": true } }, "required": [ - "box_code", - "request_id" + "id", + "is_active", + "is_overridden", + "name" ] }, - "KioskEvidencePrepare": { + "MachineTypePolicy": { "type": "object", "properties": { - "evidence_type": { - "$ref": "#/components/schemas/EvidenceUrlRequestEvidenceTypeEnum" + "cert_reminder_days": { + "type": "integer", + "maximum": 365, + "minimum": 0, + "nullable": true }, - "content_type": { - "type": "string" + "reminder_lead_days": { + "type": "integer", + "maximum": 365, + "minimum": 0, + "nullable": true }, - "size_bytes": { + "grace_days": { "type": "integer", + "maximum": 365, "minimum": 0, "nullable": true } - }, - "required": [ - "content_type", - "evidence_type" - ] + } }, - "KioskOnlineCommand": { + "MachineTypePricing": { "type": "object", "properties": { - "version": { - "$ref": "#/components/schemas/VersionEnum" + "machine_type_id": { + "type": "integer", + "readOnly": true }, - "kind": { - "$ref": "#/components/schemas/KioskOnlineCommandKindEnum" + "rate_per_unit": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true }, - "payload": { - "type": "object", - "additionalProperties": {} + "flat_fee": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true + }, + "payment_enabled": { + "type": "boolean", + "readOnly": true } }, "required": [ - "kind", - "payload", - "version" + "flat_fee", + "machine_type_id", + "payment_enabled", + "rate_per_unit" ] }, - "KioskOnlineCommandKindEnum": { - "enum": [ - "assign_box", - "direct_handout", - "direct_return", - "resolve_qr", - "reviewed_issue", - "reviewed_return" - ], - "type": "string", - "description": "* `assign_box` - assign_box\n* `direct_handout` - direct_handout\n* `direct_return` - direct_return\n* `resolve_qr` - resolve_qr\n* `reviewed_issue` - reviewed_issue\n* `reviewed_return` - reviewed_return" - }, - "KioskOnlineCommandResult": { + "MachineTypePricingList": { "type": "object", "properties": { - "kind": { - "type": "string" + "currency": { + "type": "string", + "readOnly": true }, - "result": { - "type": "object", - "additionalProperties": {} + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineTypePricing" + }, + "readOnly": true } }, "required": [ - "kind", - "result" + "currency", + "results" ] }, - "KioskOperatorLease": { + "MachineTypePricingSet": { "type": "object", "properties": { - "lease_token": { - "type": "string" - }, - "lease_id": { + "rate_per_unit": { "type": "string", - "format": "uuid" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "session_id": { + "flat_fee": { "type": "string", - "format": "uuid" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "actor_id": { + "payment_enabled": { + "type": "boolean" + } + }, + "required": [ + "flat_fee", + "payment_enabled", + "rate_per_unit" + ] + }, + "MachineUsageCandidatesPage": { + "type": "object", + "properties": { + "count": { "type": "integer" }, - "expires_at": { + "next": { "type": "string", - "format": "date-time" + "format": "uri", + "nullable": true }, - "allowed_actions": { - "type": "array", - "items": { - "type": "string" - } + "previous": { + "type": "string", + "format": "uri", + "nullable": true }, - "permitted_operations": { + "results": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/OperatorCandidate" } } }, "required": [ - "actor_id", - "allowed_actions", - "expires_at", - "lease_id", - "lease_token", - "permitted_operations", - "session_id" + "count", + "next", + "previous", + "results" ] }, - "KioskStation": { + "MachineUsageEntry": { "type": "object", "properties": { "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "kind": { - "type": "string" - }, - "enabled": { - "type": "boolean" + "type": "integer", + "readOnly": true }, - "public_token": { + "hours": { "type": "string", - "format": "uuid", - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", + "readOnly": true }, - "version": { + "member": { "type": "integer", + "readOnly": true, "nullable": true }, - "station_url": { + "started_at": { "type": "string", + "format": "date-time", + "readOnly": true, "nullable": true }, - "rotated_at": { + "source": { + "allOf": [ + { + "$ref": "#/components/schemas/MachineUsageEntrySourceEnum" + } + ], + "readOnly": true + }, + "note": { "type": "string", - "format": "date-time", + "readOnly": true + }, + "logged_by_username": { + "type": "string", + "readOnly": true, "nullable": true }, "created_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "readOnly": true } }, "required": [ "created_at", - "enabled", + "hours", "id", - "kind", - "name", - "public_token", - "rotated_at", - "station_url", - "version" - ] - }, - "KioskStationBootstrap": { - "type": "object", - "properties": { - "station": { - "$ref": "#/components/schemas/_StationRef" - }, - "makerspace": { - "$ref": "#/components/schemas/_MakerspaceRef" - }, - "session_expires_at": { - "type": "string", - "format": "date-time" - }, - "permitted_operations": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "makerspace", - "permitted_operations", - "session_expires_at", - "station" - ] - }, - "KioskStationCreate": { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 160 - }, - "kind": { - "$ref": "#/components/schemas/KioskStationCreateKindEnum" - } - }, - "required": [ - "kind", - "name" + "logged_by_username", + "member", + "note", + "source", + "started_at" ] }, - "KioskStationCreateKindEnum": { + "MachineUsageEntrySourceEnum": { "enum": [ - "scanner", - "handover" + "manual", + "typed_manual" ], "type": "string", - "description": "* `scanner` - Scanner\n* `handover` - Handover" + "description": "* `manual` - Manual hours\n* `typed_manual` - Typed manual service" }, - "KioskStationPin": { + "MachineUsageHeatmapReport": { "type": "object", "properties": { - "pin": { - "type": "string", - "writeOnly": true, - "pattern": "^\\d{8}$" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "makerspace_slug": { - "type": "string", - "maxLength": 80, - "pattern": "^[-a-zA-Z0-9_]+$" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineHeatmapRow" + } } }, "required": [ - "pin" + "rows", + "typed_rows" ] }, - "KioskStationReveal": { + "MachineUsageMembersReport": { "type": "object", "properties": { - "current_password": { - "type": "string", - "writeOnly": true + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineMemberRow" + } } }, "required": [ - "current_password" + "rows", + "typed_rows" ] }, - "KioskStationRevealResponse": { + "MachineUsageV2Report": { "type": "object", "properties": { - "pin": { - "type": "string" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "version": { - "type": "integer" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MachineUsageV2Row" + } } }, "required": [ - "pin", - "version" + "rows", + "typed_rows" ] }, - "KioskStationSecret": { + "MachineUsageV2Row": { "type": "object", "properties": { - "id": { + "makerspace_id": { "type": "integer" }, - "name": { - "type": "string" + "period": { + "type": "string", + "format": "date", + "nullable": true }, - "kind": { - "type": "string" + "machine_id": { + "type": "integer", + "nullable": true }, - "enabled": { - "type": "boolean" + "machine_name": { + "type": "string", + "nullable": true }, - "public_token": { + "machine_type": { "type": "string", - "format": "uuid", "nullable": true }, - "version": { + "measure": { + "$ref": "#/components/schemas/MeasureEnum" + }, + "machine_type_id": { "type": "integer", "nullable": true }, - "station_url": { + "entries": { + "type": "integer" + }, + "service_hours": { "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true }, - "rotated_at": { + "manual_hours": { "type": "string", - "format": "date-time", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", "nullable": true }, - "created_at": { + "booked_hours": { "type": "string", - "format": "date-time" - }, - "pin": { - "type": "string" + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true } }, "required": [ - "created_at", - "enabled", - "id", - "kind", - "name", - "pin", - "public_token", - "rotated_at", - "station_url", - "version" + "booked_hours", + "entries", + "machine_id", + "machine_name", + "machine_type", + "machine_type_id", + "manual_hours", + "measure", + "period", + "service_hours" ] }, - "KioskWorkLine": { + "MaintenanceActivityV2Report": { "type": "object", "properties": { - "item_id": { - "type": "integer" - }, - "label": { - "type": "string" - }, - "quantity": { - "type": "integer" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "returned": { - "type": "integer" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaintenanceActivityV2Row" + } } }, "required": [ - "item_id", - "label", - "quantity", - "returned" + "rows", + "typed_rows" ] }, - "KioskWorkPackage": { + "MaintenanceActivityV2Row": { "type": "object", "properties": { - "kind": { - "type": "string" - }, - "id": { + "makerspace_id": { "type": "integer" }, - "status": { - "type": "string" + "period": { + "type": "string", + "format": "date", + "nullable": true }, - "display_label": { - "type": "string" + "machine_id": { + "type": "integer", + "nullable": true + }, + "machine_name": { + "type": "string", + "nullable": true + }, + "machine_type": { + "type": "string", + "nullable": true + }, + "is_active": { + "type": "boolean" + }, + "log_count": { + "type": "integer", + "nullable": true + }, + "costed_log_count": { + "type": "integer", + "nullable": true + }, + "total_cost": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true }, - "person_display": { - "type": "string" + "average_cost": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true }, - "box": { + "last_performed_at": { "type": "string", + "format": "date-time", "nullable": true }, - "lines": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KioskWorkLine" - } + "average_interval_days": { + "type": "number", + "format": "double", + "nullable": true }, - "kit_components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitExpectation" - } + "active_schedules": { + "type": "integer", + "nullable": true }, - "return_resolutions": { - "type": "array", - "items": { - "type": "string" - } + "overdue_schedules": { + "type": "integer", + "nullable": true }, - "expires_at": { + "currency": { "type": "string", - "format": "date-time" - }, - "command_age_limit_seconds": { - "type": "integer" - }, - "evidence_max_bytes": { - "type": "integer" + "nullable": true, + "description": "Unknown: legacy logs do not record currency. No cross-machine money total." } }, "required": [ - "box", - "command_age_limit_seconds", - "display_label", - "evidence_max_bytes", - "expires_at", - "id", - "kind", - "kit_components", - "lines", - "person_display", - "return_resolutions", - "status" + "active_schedules", + "average_cost", + "average_interval_days", + "costed_log_count", + "currency", + "is_active", + "last_performed_at", + "log_count", + "machine_id", + "machine_name", + "machine_type", + "overdue_schedules", + "period", + "total_cost" ] }, - "KioskWorkPackageRequest": { + "MaintenanceDocumentFinalize": { "type": "object", "properties": { - "kind": { - "$ref": "#/components/schemas/KioskWorkPackageRequestKindEnum" - }, - "id": { - "type": "integer", - "minimum": 1 + "object_key": { + "type": "string", + "maxLength": 500 } }, "required": [ - "id", - "kind" + "object_key" ] }, - "KioskWorkPackageRequestKindEnum": { - "enum": [ - "reviewed_request", - "direct_loan", - "direct_handout" - ], - "type": "string", - "description": "* `reviewed_request` - reviewed_request\n* `direct_loan` - direct_loan\n* `direct_handout` - direct_handout" - }, - "KitComponent": { + "MaintenanceDocumentPresign": { "type": "object", "properties": { - "key": { + "filename": { "type": "string", - "format": "uuid" + "maxLength": 255 }, - "label": { + "content_type": { "type": "string", - "maxLength": 200 - }, - "quantity": { - "type": "integer", - "maximum": 999, - "minimum": 1 + "maxLength": 100 } }, "required": [ - "label", - "quantity" + "content_type", + "filename" ] }, - "KitCount": { + "MaintenanceDocumentPresignResponse": { "type": "object", "properties": { - "key": { - "type": "string", - "format": "uuid" + "object_key": { + "type": "string" }, - "present": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "upload": { + "$ref": "#/components/schemas/MaintenanceDocumentUpload" } }, "required": [ - "key", - "present" + "object_key", + "upload" ] }, - "KitExpectation": { + "MaintenanceDocumentUpload": { "type": "object", "properties": { - "key": { + "url": { "type": "string", - "format": "uuid" - }, - "label": { - "type": "string" + "format": "uri" }, - "owner_label": { + "method": { "type": "string" }, - "quantity": { - "type": "integer" + "fields": { + "type": "object", + "additionalProperties": {} }, - "issue_present": { - "type": "integer" + "headers": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "key", - "label", - "owner_label", - "quantity" + "url" ] }, - "KitPreview": { + "MaintenanceDocumentUrl": { "type": "object", "properties": { - "components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitExpectation" - } + "url": { + "type": "string", + "format": "uri" } }, "required": [ - "components" + "url" ] }, - "KitPreviewInput": { + "MaintenanceLog": { "type": "object", "properties": { - "phase": { - "allOf": [ - { - "$ref": "#/components/schemas/PhaseEnum" - } - ], - "default": "issue" - }, - "request_id": { + "id": { "type": "integer", - "minimum": 1 + "readOnly": true }, - "loan_id": { + "machine_id": { "type": "integer", - "minimum": 1 - }, - "box_code": { - "type": "string" + "readOnly": true }, - "container_id": { + "performed_by_id": { "type": "integer", - "minimum": 1, + "readOnly": true, "nullable": true }, - "qr_payloads": { - "type": "array", - "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 50 - }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DirectLoanItem" - } - }, - "rejects": { - "type": "array", - "items": { - "$ref": "#/components/schemas/IssueReject" - } - }, - "resolutions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReturnItemResolution" - } - } - } - }, - "LabelCommand": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "command_id": { + "performed_at": { "type": "string", - "format": "uuid", + "format": "date-time", "readOnly": true }, - "command_status": { + "summary": { "type": "string", "readOnly": true }, - "device_id": { + "cost": { "type": "string", - "format": "uuid", - "readOnly": true - }, - "batch_id": { - "type": "integer", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true, "nullable": true }, - "request_id": { + "parts_note": { "type": "string", - "format": "uuid", - "readOnly": true - }, - "batch_id_snapshot": { - "type": "integer", "readOnly": true }, - "batch_title_snapshot": { + "created_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "item_count_snapshot": { + "documents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaintenanceLogDocument" + }, + "readOnly": true + } + }, + "required": [ + "cost", + "created_at", + "documents", + "id", + "machine_id", + "parts_note", + "performed_at", + "performed_by_id", + "summary" + ] + }, + "MaintenanceLogDocument": { + "type": "object", + "properties": { + "id": { "type": "integer", "readOnly": true }, - "result": { - "allOf": [ - { - "$ref": "#/components/schemas/ResultEnum" - } - ], + "log_id": { + "type": "integer", "readOnly": true }, - "result_code": { + "object_key": { "type": "string", "readOnly": true }, - "result_message": { - "type": "string", + "size_bytes": { + "type": "integer", "readOnly": true }, - "completed_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "requested_by_id": { + "uploaded_by_id": { "type": "integer", "readOnly": true, "nullable": true @@ -80028,208 +102238,174 @@ } }, "required": [ - "batch_id", - "batch_id_snapshot", - "batch_title_snapshot", - "command_id", - "command_status", - "completed_at", "created_at", - "device_id", "id", - "item_count_snapshot", - "request_id", - "requested_by_id", - "result", - "result_code", - "result_message" + "log_id", + "object_key", + "size_bytes", + "uploaded_by_id" ] }, - "LabelCreate": { + "MaintenanceLogList": { "type": "object", "properties": { - "device_id": { + "count": { + "type": "integer" + }, + "next": { "type": "string", - "format": "uuid" + "nullable": true }, - "request_id": { + "previous": { "type": "string", - "format": "uuid" + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaintenanceLog" + } } }, "required": [ - "device_id", - "request_id" + "count", + "results" ] }, - "LabelReceipt": { + "MaintenanceLogWrite": { "type": "object", "properties": { - "operation_id": { - "type": "string", - "format": "uuid" + "summary": { + "type": "string" }, - "label_command_id": { + "performed_at": { "type": "string", - "format": "uuid" - }, - "outcome": { - "$ref": "#/components/schemas/OutcomeA1aEnum" - }, - "result_code": { - "$ref": "#/components/schemas/ResultCodeEnum" - }, - "response_result": { - "$ref": "#/components/schemas/ResponseResultEnum" - }, - "response_command_status": { - "type": "string" + "format": "date-time" }, - "response_completed_at": { + "cost": { "type": "string", - "format": "date-time", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "nullable": true - } - }, - "required": [ - "label_command_id", - "operation_id", - "outcome", - "response_command_status", - "response_completed_at", - "response_result", - "result_code" - ] - }, - "LabelResult": { - "type": "object", - "properties": { - "operation_id": { + }, + "parts_note": { "type": "string", - "format": "uuid" + "default": "" }, - "outcome": { - "$ref": "#/components/schemas/OutcomeA1aEnum" + "set_idle": { + "type": "boolean", + "default": false }, - "result_code": { - "$ref": "#/components/schemas/ResultCodeEnum" - } - }, - "required": [ - "operation_id", - "outcome", - "result_code" - ] - }, - "LeaseResponse": { - "type": "object", - "properties": { - "command": { - "allOf": [ - { - "$ref": "#/components/schemas/CommandLease" - } - ], - "nullable": true + "schedule_id": { + "type": "integer", + "minimum": 1, + "writeOnly": true } }, "required": [ - "command" + "summary" ] }, - "LedgerLine": { + "MaintenanceSchedule": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" + "id": { + "type": "integer", + "readOnly": true }, - "currency": { - "type": "string" + "machine_id": { + "type": "integer", + "readOnly": true }, - "collected_amount": { + "description": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "readOnly": true }, - "refunded_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "interval_days": { + "type": "integer", + "readOnly": true }, - "date": { + "next_due": { "type": "string", - "format": "date-time" - }, - "row_kind": { - "type": "string" - }, - "row_id": { - "type": "integer" - }, - "payment_id": { - "type": "integer" + "format": "date", + "readOnly": true }, - "document_number": { - "type": "string" + "anchor": { + "allOf": [ + { + "$ref": "#/components/schemas/AnchorEnum" + } + ], + "readOnly": true }, - "subject_type": { - "type": "string" + "trigger": { + "allOf": [ + { + "$ref": "#/components/schemas/MaintenanceScheduleTriggerEnum" + } + ], + "readOnly": true }, - "payer_type": { - "type": "string" + "usage_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true, + "nullable": true }, - "payer_id": { + "job_count": { "type": "integer", + "readOnly": true, "nullable": true }, - "status": { - "type": "string" - }, - "amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" - }, - "owed_amount": { + "meter_started_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "format": "date-time", + "readOnly": true }, - "method": { - "type": "string" + "is_active": { + "type": "boolean", + "readOnly": true }, - "reference": { - "type": "string" + "created_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "effective": { - "type": "boolean" + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "refund_status": { - "type": "string" + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "overdue": { + "type": "boolean", + "readOnly": true } }, "required": [ - "amount", - "collected_amount", - "currency", - "date", - "document_number", - "effective", - "method", - "owed_amount", - "payer_id", - "payer_type", - "payment_id", - "reference", - "refund_status", - "refunded_amount", - "row_id", - "row_kind", - "status", - "subject_type" + "anchor", + "created_at", + "created_by_id", + "description", + "id", + "interval_days", + "is_active", + "job_count", + "machine_id", + "meter_started_at", + "next_due", + "overdue", + "trigger", + "updated_at", + "usage_hours" ] }, - "LedgerResponse": { + "MaintenanceScheduleList": { "type": "object", "properties": { "count": { @@ -80246,7 +102422,7 @@ "results": { "type": "array", "items": { - "$ref": "#/components/schemas/LedgerRow" + "$ref": "#/components/schemas/MaintenanceSchedule" } } }, @@ -80255,3852 +102431,3902 @@ "results" ] }, - "LedgerRow": { + "MaintenanceScheduleTriggerEnum": { + "enum": [ + "days", + "usage", + "both" + ], + "type": "string", + "description": "* `days` - Days\n* `usage` - Usage\n* `both` - Days or usage" + }, + "MaintenanceScheduleWrite": { "type": "object", "properties": { - "source": { - "$ref": "#/components/schemas/LedgerRowSourceEnum" - }, - "item_name": { - "type": "string" - }, - "container": { - "type": "string", - "nullable": true - }, - "holder": { + "description": { "type": "string" }, - "quantity": { - "type": "integer" - }, - "units": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LedgerUnit" - } + "interval_days": { + "type": "integer", + "minimum": 1 }, - "target_label": { + "next_due": { "type": "string", - "nullable": true + "format": "date" }, - "since": { - "type": "string", - "format": "date-time", - "nullable": true + "trigger": { + "$ref": "#/components/schemas/MaintenanceScheduleWriteTriggerEnum" }, - "due": { + "usage_hours": { "type": "string", - "format": "date-time", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "nullable": true }, - "makerspace_id": { - "type": "integer" - }, - "reference_id": { - "type": "integer" + "job_count": { + "type": "integer", + "minimum": 1, + "nullable": true }, - "status": { - "type": "string" + "anchor": { + "$ref": "#/components/schemas/AnchorEnum" } }, "required": [ - "due", - "holder", - "item_name", - "makerspace_id", - "quantity", - "reference_id", - "since", - "source", - "status", - "units" + "description", + "interval_days", + "next_due" ] }, - "LedgerRowSourceEnum": { + "MaintenanceScheduleWriteTriggerEnum": { "enum": [ - "request", - "self_checkout", - "direct_handout" + "days", + "usage", + "both" ], "type": "string", - "description": "* `request` - request\n* `self_checkout` - self_checkout\n* `direct_handout` - direct_handout" - }, - "LedgerUnit": { - "type": "object", - "properties": { - "asset_tag": { - "type": "string" - }, - "serial_number": { - "type": "string" - } - }, - "required": [ - "asset_tag", - "serial_number" - ] + "description": "* `days` - days\n* `usage` - usage\n* `both` - both" }, - "LegacyResetPasswordConfirm": { + "Makerspace": { "type": "object", "properties": { - "uid": { - "type": "string" + "organization_id": { + "type": "integer", + "minimum": 1, + "writeOnly": true, + "description": "Existing active owner organization ID. On create, supply this or organization_name, exactly one; refused on update." }, - "token": { + "organization_name": { "type": "string", + "writeOnly": true, + "description": "Create a new owner organization with this name. On create, supply this or organization_id, exactly one; refused on update.", + "maxLength": 200 + }, + "creation_setup": { + "allOf": [ + { + "$ref": "#/components/schemas/MakerspaceCreationSetup" + } + ], "writeOnly": true }, - "new_password": { - "type": "string", + "creation_invitation": { + "allOf": [ + { + "$ref": "#/components/schemas/CreationInviteDelivery" + } + ], + "readOnly": true, + "nullable": true + }, + "install_profile_id": { + "type": "integer", + "minimum": 1, "writeOnly": true - } - }, - "required": [ - "new_password", - "token", - "uid" - ] - }, - "LegacyRoleEnum": { - "enum": [ - "space_manager", - "inventory_manager", - "print_manager", - "machine_manager" - ], - "type": "string", - "description": "* `space_manager` - Space Manager\n* `inventory_manager` - Inventory Manager\n* `print_manager` - Print Manager\n* `machine_manager` - Machine Manager" - }, - "LendingHistoryActor": { - "type": "object", - "properties": { - "username": { - "type": "string" }, - "role": { - "type": "string" - } - }, - "required": [ - "role", - "username" - ] - }, - "LendingHistoryEntry": { - "type": "object", - "properties": { - "id": { - "type": "integer" + "id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "maxLength": 200 + }, + "public_code": { + "type": "string", + "pattern": "^[A-Z0-9]{4}$", + "maxLength": 4 + }, + "slug": { + "type": "string", + "maxLength": 50, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "location": { + "type": "string", + "maxLength": 200 + }, + "timezone": { + "type": "string", + "readOnly": true + }, + "country": { + "type": "string", + "readOnly": true + }, + "opening_hours": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "required": [ + "weekly", + "exceptions" + ], + "additionalProperties": false, + "properties": { + "weekly": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + "sun" + ], + "properties": { + "mon": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "tue": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "wed": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "thu": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "fri": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sat": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sun": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + } + } + } + ] + }, + "exceptions": { + "type": "object", + "maxProperties": 366, + "additionalProperties": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." + } + } + } + ], + "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days.", + "readOnly": true + }, + "map_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "geofence_latitude": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,3}(?:\\.\\d{0,6})?$", + "nullable": true + }, + "geofence_longitude": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,3}(?:\\.\\d{0,6})?$", + "nullable": true + }, + "geofence_radius_m": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1 + }, + "geofence_enabled": { + "type": "boolean" + }, + "public_inventory_enabled": { + "type": "boolean" + }, + "public_site_enabled": { + "type": "boolean" + }, + "public_landing_page": { + "$ref": "#/components/schemas/PublicLandingPage6a0Enum" + }, + "public_stats_enabled": { + "type": "boolean" + }, + "public_stats_show_holder_names": { + "type": "boolean" + }, + "public_print_status_lookup_policy": { + "$ref": "#/components/schemas/PublicPrintStatusLookupPolicyEnum" + }, + "membership_policy": { + "$ref": "#/components/schemas/MembershipPolicyEnum" + }, + "membership_dues_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "username": { - "type": "string" + "referrals_enabled": { + "type": "boolean" }, - "issued_at": { + "lapsed_members_cannot_borrow": { + "type": "boolean" + }, + "anonymous_requests_enabled": { + "type": "boolean" + }, + "request_access": { "type": "string", - "format": "date-time" + "readOnly": true }, - "quantity": { - "type": "integer" + "filament_low_stock_threshold_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$" }, - "issued_by": { + "superadmin_access_enabled": { + "type": "boolean" + }, + "archive_custody_state": { "allOf": [ { - "$ref": "#/components/schemas/LendingHistoryActor" + "$ref": "#/components/schemas/ArchiveCustodyStateEnum" } ], - "nullable": true + "nullable": true, + "readOnly": true }, - "accepted_by": { + "staff_notifications_enabled": { + "type": "boolean" + }, + "booking_requester_notifications_enabled": { + "type": "boolean" + }, + "event_default_registration_audience": { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + }, + "event_reminder_lead_hours": { + "type": "array", + "items": { + "type": "integer", + "maximum": 8760, + "minimum": 1 + }, + "maxItems": 100 + }, + "booking_reminder_lead_hours": { + "type": "array", + "items": { + "type": "integer", + "maximum": 8760, + "minimum": 1 + }, + "maxItems": 100 + }, + "logo_key": { + "type": "string", + "readOnly": true + }, + "logo_url": { + "type": "string", + "format": "uri", + "nullable": true, + "readOnly": true + }, + "cover_image_key": { + "type": "string", + "readOnly": true + }, + "cover_image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "readOnly": true + }, + "frontend_domain": { + "type": "string", + "nullable": true, + "maxLength": 255 + }, + "frontend_domain_status": { "allOf": [ { - "$ref": "#/components/schemas/LendingHistoryActor" + "$ref": "#/components/schemas/FrontendDomainStatusEnum" } ], + "readOnly": true + }, + "domain_verified_at": { + "type": "string", + "format": "date-time", + "readOnly": true, "nullable": true - } - }, - "required": [ - "accepted_by", - "id", - "issued_at", - "issued_by", - "quantity", - "username" - ] - }, - "LendingHistoryResponse": { - "type": "object", - "properties": { - "product_id": { - "type": "integer" }, - "last_borrower": { - "allOf": [ - { - "$ref": "#/components/schemas/LendingHistoryEntry" + "domain_verification_token": { + "type": "string", + "readOnly": true + }, + "domain_verification_record": { + "type": "object", + "nullable": true, + "properties": { + "host": { + "type": "string" + }, + "type": { + "type": "string" + }, + "value": { + "type": "string" } - ], - "nullable": true + }, + "readOnly": true }, - "recent": { + "platform_hosting": { + "type": "boolean", + "readOnly": true + }, + "is_platform_subdomain": { + "type": "boolean", + "readOnly": true + }, + "hidden_from_central_directory": { + "type": "boolean" + }, + "public_api_key": { + "type": "string", + "readOnly": true + }, + "cors_allowed_origins": { + "readOnly": true + }, + "enabled_modules": { "type": "array", "items": { - "$ref": "#/components/schemas/LendingHistoryEntry" + "type": "string" + }, + "readOnly": true + }, + "unavailable_apps": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "resource_limit_overrides": {}, + "enabled_features": {}, + "theme_config": { + "type": "object", + "additionalProperties": false, + "description": "Public/member brand colours. Labels and light/dark hover, active, soft and link colours are derived automatically with accessible fallbacks. Well-formed #RGB or #RRGGBB colours are accepted; only malformed colour values are rejected. Blank colours use Maker Teal. Staff chrome and status colours stay fixed.", + "properties": { + "mode": { + "type": "string", + "enum": [ + "system", + "light", + "dark" + ] + }, + "primary_color": { + "type": "string", + "pattern": "^$|^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + }, + "accent_color": { + "type": "string", + "pattern": "^$|^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + }, + "logo_url": { + "type": "string", + "maxLength": 2048 + } } - } - }, - "required": [ - "last_borrower", - "product_id", - "recent" - ] - }, - "LevelEnum": { - "enum": [ - "info", - "warning", - "critical" - ], - "type": "string", - "description": "* `info` - Info\n* `warning` - Warning\n* `critical` - Critical" - }, - "LinkMachineConsumable": { - "type": "object", - "properties": { - "measurement": { - "$ref": "#/components/schemas/Measurement883Enum" }, - "product_id": { - "type": "integer", - "nullable": true + "branding_config": { + "readOnly": true }, - "label": { + "public_display_name": { "type": "string", + "writeOnly": true, "maxLength": 200 }, - "remaining": { + "telegram_group_chat_id": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "maxLength": 64 }, - "low_threshold": { + "telegram_bot_token": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "nullable": true + "writeOnly": true }, - "note": { - "type": "string", - "maxLength": 255 - } - }, - "required": [ - "measurement" - ] - }, - "LoanDepositModeEnum": { - "enum": [ - "none", - "fixed", - "per_product" - ], - "type": "string", - "description": "* `none` - No deposit\n* `fixed` - Fixed amount per loan\n* `per_product` - Sum of product deposits" - }, - "LoanRenewal": { - "type": "object", - "properties": { - "id": { - "type": "integer" + "telegram_bot_token_set": { + "type": "boolean", + "readOnly": true }, - "return_due_at": { + "smtp_host": { "type": "string", - "format": "date-time" + "maxLength": 200 }, - "renewal_count": { - "type": "integer" - } - }, - "required": [ - "id", - "renewal_count", - "return_due_at" - ] - }, - "LoanRenewalModeEnum": { - "enum": [ - "off", - "auto" - ], - "type": "string", - "description": "* `off` - Off\n* `auto` - Automatic within policy" - }, - "LocationKindEnum": { - "enum": [ - "indoor", - "outdoor", - "other" - ], - "type": "string", - "description": "* `indoor` - Indoor\n* `outdoor` - Outdoor\n* `other` - Other" - }, - "LogError": { - "type": "object", - "properties": { - "severity": { + "smtp_port": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "smtp_username": { "type": "string", - "maxLength": 16 + "maxLength": 200 }, - "message": { - "type": "string" - } - }, - "required": [ - "message", - "severity" - ] - }, - "LogMachineConsumption": { - "type": "object", - "properties": { - "quantity": { + "smtp_password": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - } - }, - "required": [ - "quantity" - ] - }, - "LogUsage": { - "type": "object", - "properties": { - "hours": { + "writeOnly": true + }, + "smtp_password_set": { + "type": "boolean", + "readOnly": true + }, + "smtp_use_tls": { + "type": "boolean" + }, + "smtp_use_ssl": { + "type": "boolean" + }, + "smtp_from_email": { + "oneOf": [ + { + "type": "string", + "format": "email", + "maxLength": 254 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "slack_webhook_url": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$" + "writeOnly": true, + "maxLength": 2048 }, - "note": { + "slack_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "mattermost_webhook_url": { "type": "string", - "maxLength": 255 - } - }, - "required": [ - "hours" - ] - }, - "LoginRequest": { - "type": "object", - "properties": { - "username": { - "type": "string" + "writeOnly": true, + "maxLength": 2048 }, - "password": { + "mattermost_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "discord_webhook_url": { "type": "string", - "writeOnly": true + "writeOnly": true, + "maxLength": 2048 }, - "surface": { - "$ref": "#/components/schemas/LoginRequestSurfaceEnum" - } - }, - "required": [ - "password", - "username" - ] - }, - "LoginRequestSurfaceEnum": { - "enum": [ - "member", - "staff" - ], - "type": "string", - "description": "* `member` - member\n* `staff` - staff" - }, - "LoginResponse": { - "type": "object", - "properties": { - "user": { - "$ref": "#/components/schemas/AuthUserPayload" + "discord_webhook_url_set": { + "type": "boolean", + "readOnly": true }, - "access": { - "type": "string" + "default_loan_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "surface": { - "$ref": "#/components/schemas/LoginResponseSurfaceEnum" - } - }, - "required": [ - "access", - "surface", - "user" - ] - }, - "LoginResponseSurfaceEnum": { - "enum": [ - "member", - "staff", - "staff_api", - "verification_only" - ], - "type": "string", - "description": "* `member` - member\n* `staff` - staff\n* `staff_api` - staff_api\n* `verification_only` - verification_only" - }, - "LogoutResponse": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - }, - "required": [ - "detail" - ] - }, - "MFAChallenge": { - "type": "object", - "properties": { - "challenge": { + "presence_preset_minutes": {}, + "created_at": { "type": "string", - "writeOnly": true, - "maxLength": 200, - "minLength": 20 + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ - "challenge" + "archive_custody_state", + "branding_config", + "cors_allowed_origins", + "country", + "cover_image_key", + "cover_image_url", + "created_at", + "creation_invitation", + "discord_webhook_url_set", + "domain_verification_record", + "domain_verification_token", + "domain_verified_at", + "enabled_modules", + "frontend_domain_status", + "id", + "is_platform_subdomain", + "logo_key", + "logo_url", + "mattermost_webhook_url_set", + "name", + "opening_hours", + "platform_hosting", + "public_api_key", + "request_access", + "slack_webhook_url_set", + "slug", + "smtp_password_set", + "telegram_bot_token_set", + "timezone", + "unavailable_apps", + "updated_at" ] }, - "MFAChallengeResponse": { + "MakerspaceArchiveRequest": { "type": "object", "properties": { - "mfa_required": { - "type": "boolean" + "id": { + "type": "integer", + "readOnly": true }, - "enrollment_required": { - "type": "boolean" + "makerspace": { + "type": "integer", + "readOnly": true }, - "challenge": { - "type": "string" + "requested_by": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "expires_in": { - "type": "integer" - } - }, - "required": [ - "challenge", - "enrollment_required", - "expires_in", - "mfa_required" - ] - }, - "MFACompleted": { - "type": "object", - "properties": { - "user": { - "$ref": "#/components/schemas/AuthUserPayload" + "requested_by_username": { + "type": "string", + "readOnly": true, + "nullable": true }, - "access": { - "type": "string" + "requested_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "surface": { - "type": "string" + "resolved_by": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "refresh": { - "type": "string" + "resolved_by_username": { + "type": "string", + "readOnly": true, + "nullable": true }, - "device_grant": { - "type": "object", - "additionalProperties": {} + "resolved_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true }, - "recovery_codes": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "access", - "surface", - "user" - ] - }, - "MFAEnrollment": { - "type": "object", - "properties": { - "secret": { - "type": "string" + "reason": { + "type": "string", + "description": "Do not include personal data. Maximum 2,000 characters.", + "maxLength": 2000 }, - "provisioning_uri": { - "type": "string" + "resolution_note": { + "type": "string", + "readOnly": true }, - "qr_data_url": { - "type": "string" + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/MakerspaceArchiveRequestStatusEnum" + } + ], + "readOnly": true, + "default": "pending" } }, "required": [ - "provisioning_uri", - "qr_data_url", - "secret" + "id", + "makerspace", + "reason", + "requested_at", + "requested_by", + "requested_by_username", + "resolution_note", + "resolved_at", + "resolved_by", + "resolved_by_username", + "status" ] }, - "MFAPassword": { - "type": "object", - "properties": { - "password": { - "type": "string", - "writeOnly": true, - "maxLength": 1024 - } - }, - "required": [ - "password" - ] + "MakerspaceArchiveRequestStatusEnum": { + "enum": [ + "pending", + "approved", + "declined", + "withdrawn" + ], + "type": "string", + "description": "* `pending` - Pending\n* `approved` - Approved\n* `declined` - Declined\n* `withdrawn` - Withdrawn" }, - "MFARecovery": { + "MakerspaceCreationSetup": { "type": "object", "properties": { - "recovery_codes": { + "profile": { + "$ref": "#/components/schemas/CreationProfile" + }, + "modules": { "type": "array", "items": { - "type": "string" - } - } - }, - "required": [ - "recovery_codes" - ] - }, - "MFAStatus": { - "type": "object", - "properties": { - "available": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" + "$ref": "#/components/schemas/ModulesEnum" + }, + "maxItems": 60 }, - "required": { - "type": "boolean" + "first_staff_invite": { + "$ref": "#/components/schemas/FirstStaffInvite" } - }, - "required": [ - "available", - "enabled", - "required" - ] + } }, - "MFAVerify": { + "MakerspacePaymentSettings": { "type": "object", "properties": { - "challenge": { + "default_currency": { "type": "string", - "writeOnly": true, - "maxLength": 200, - "minLength": 20 + "maxLength": 3 }, - "code": { + "stripe_publishable_key": { "type": "string", "writeOnly": true, - "maxLength": 64, - "minLength": 6 - } - }, - "required": [ - "challenge", - "code" - ] - }, - "Machine": { - "type": "object", - "properties": { - "id": { - "type": "integer", + "maxLength": 255 + }, + "stripe_publishable_key_set": { + "type": "boolean", "readOnly": true }, - "makerspace": { - "type": "integer", + "stripe_secret_key": { + "type": "string", + "writeOnly": true + }, + "stripe_secret_key_set": { + "type": "boolean", "readOnly": true }, - "machine_type": { + "stripe_webhook_secret": { + "type": "string", + "writeOnly": true + }, + "stripe_webhook_secret_set": { + "type": "boolean", + "readOnly": true + }, + "connect_account_id": { + "type": "string", + "readOnly": true, + "nullable": true + }, + "connect_status": { "allOf": [ { - "$ref": "#/components/schemas/MachineType" + "$ref": "#/components/schemas/ConnectStatusEnum" } ], "readOnly": true }, - "machine_type_id": { - "type": "integer", - "writeOnly": true + "connect_charges_enabled": { + "type": "boolean", + "readOnly": true }, - "name": { + "connect_payouts_enabled": { + "type": "boolean", + "readOnly": true + }, + "connect_status_updated_at": { "type": "string", - "maxLength": 200 + "format": "date-time", + "readOnly": true }, - "location": { + "effective_mode": { "type": "string", - "maxLength": 200 + "readOnly": true }, - "notes": { - "type": "string" + "loan_deposit_mode": { + "$ref": "#/components/schemas/LoanDepositModeEnum" }, - "status": { - "allOf": [ + "loan_deposit_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "loan_late_fee_per_day": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "loan_late_fee_cap": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "loan_grace_days": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "loan_deposit_blocks_issue": { + "type": "boolean" + } + }, + "required": [ + "connect_account_id", + "connect_charges_enabled", + "connect_payouts_enabled", + "connect_status", + "connect_status_updated_at", + "effective_mode", + "stripe_publishable_key_set", + "stripe_secret_key_set", + "stripe_webhook_secret_set" + ] + }, + "MakerspaceProfile": { + "type": "object", + "properties": { + "timezone": { + "type": "string", + "maxLength": 100 + }, + "country": { + "type": "string", + "maxLength": 2 + }, + "opening_hours": { + "oneOf": [ { - "$ref": "#/components/schemas/MachineStatusEnum" + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "required": [ + "weekly", + "exceptions" + ], + "additionalProperties": false, + "properties": { + "weekly": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + "sun" + ], + "properties": { + "mon": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "tue": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "wed": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "thu": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "fri": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sat": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sun": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + } + } + } + ] + }, + "exceptions": { + "type": "object", + "maxProperties": 366, + "additionalProperties": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." + } + } } ], - "readOnly": true - }, - "firmware_version": { - "type": "string", - "maxLength": 100 - }, - "camera_feed_url": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "status_since": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "public_fields": {}, - "type_payload": {}, - "image_url": { - "type": "string", - "nullable": true, - "readOnly": true + "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." }, - "warranty_status": { + "default_currency": { "type": "string", - "readOnly": true - }, - "is_public": { - "type": "boolean", - "readOnly": true - }, - "is_active": { - "type": "boolean", - "readOnly": true - }, - "usage_hours": { - "type": "number", - "format": "double", - "readOnly": true - }, - "can_operate": { - "type": "boolean", - "readOnly": true - }, - "can_edit": { - "type": "boolean", - "readOnly": true - }, - "can_delegate": { - "type": "boolean", - "readOnly": true - }, - "can_retire": { - "type": "boolean", - "readOnly": true - }, - "can_unretire": { - "type": "boolean", - "readOnly": true - }, - "can_manage": { - "type": "boolean", - "readOnly": true + "maxLength": 3 }, - "created_at": { - "type": "string", - "format": "date-time", + "timezone_choices": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true }, - "updated_at": { - "type": "string", - "format": "date-time", + "country_choices": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true } }, "required": [ - "can_delegate", - "can_edit", - "can_manage", - "can_operate", - "can_retire", - "can_unretire", - "created_at", - "id", - "image_url", - "is_active", - "is_public", - "machine_type", - "machine_type_id", - "makerspace", - "name", - "status", - "status_since", - "updated_at", - "usage_hours", - "warranty_status" + "country", + "country_choices", + "default_currency", + "opening_hours", + "timezone", + "timezone_choices" ] }, - "MachineConsumable": { + "MakerspaceUsage": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "measurement": { - "allOf": [ - { - "$ref": "#/components/schemas/Measurement883Enum" - } - ], - "readOnly": true - }, - "product": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "product_name": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "available": { - "type": "integer", - "nullable": true, - "readOnly": true - }, - "remaining": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "nullable": true, - "readOnly": true - }, - "label": { - "type": "string", - "readOnly": true - }, - "low_threshold": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true, - "nullable": true + "makerspace_id": { + "type": "integer" }, - "note": { - "type": "string", - "readOnly": true + "managed": { + "type": "boolean" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "resources": { + "type": "array", + "items": { + "$ref": "#/components/schemas/UsageResource" + } } }, "required": [ - "available", - "created_at", - "id", - "label", - "low_threshold", - "measurement", - "note", - "product", - "product_name", - "remaining" + "makerspace_id", + "managed", + "resources" ] }, - "MachineDocument": { + "ManagedPolicyMarker": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "doc_type": { - "allOf": [ - { - "$ref": "#/components/schemas/DocTypeEnum" - } - ], - "readOnly": true - }, - "original_filename": { - "type": "string", - "readOnly": true + "feature": { + "type": "string" }, - "content_type": { - "type": "string", - "readOnly": true + "event": { + "type": "string" }, - "size_bytes": { + "count": { "type": "integer", - "readOnly": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "minimum": 1 } }, "required": [ - "content_type", - "created_at", - "doc_type", - "id", - "original_filename", - "size_bytes" + "count", + "event", + "feature" ] }, - "MachineErrorLog": { + "ManualRefundError": { + "oneOf": [ + { + "$ref": "#/components/schemas/HardwareRequestError" + }, + { + "$ref": "#/components/schemas/ManualRefundValidationError" + } + ] + }, + "ManualRefundReceipt": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "severity": { - "allOf": [ - { - "$ref": "#/components/schemas/SeverityEnum" - } - ], - "readOnly": true - }, - "message": { - "type": "string", - "readOnly": true + "method": { + "$ref": "#/components/schemas/Method2edEnum" }, - "logged_by_username": { + "reference": { "type": "string", - "readOnly": true, - "nullable": true + "default": "", + "maxLength": 64 }, - "created_at": { + "refunded_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" } }, "required": [ - "created_at", - "id", - "logged_by_username", - "message", - "severity" + "method", + "refunded_at" ] }, - "MachineListResponse": { + "ManualRefundValidationError": { "type": "object", "properties": { - "count": { - "type": "integer" + "amount": { + "type": "array", + "items": { + "type": "string" + } }, - "results": { + "reason": { "type": "array", "items": { - "$ref": "#/components/schemas/Machine" + "type": "string" + } + }, + "receipt": {}, + "non_field_errors": { + "type": "array", + "items": { + "type": "string" } } - }, - "required": [ - "count", - "results" - ] + } }, - "MachineOperator": { + "ManualSettlement": { "type": "object", + "description": "The receipt required to mark a charge paid offline.\n\nMethod and received date are REQUIRED: the point of the ledger is that a settled\ncharge can always say how and when the money arrived, and an optional field would\nquietly reproduce the bare boolean it replaces. `reference` stays optional because\ncash genuinely has none.", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "user": { - "type": "integer", - "readOnly": true - }, - "username": { - "type": "string", - "readOnly": true - }, - "access_level": { - "allOf": [ - { - "$ref": "#/components/schemas/AccessLevelEnum" - } - ], - "readOnly": true + "method": { + "$ref": "#/components/schemas/Method2edEnum" }, - "assigned_by_username": { + "reference": { "type": "string", - "readOnly": true, - "nullable": true + "default": "", + "maxLength": 64 }, - "assigned_at": { + "received_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" } }, "required": [ - "access_level", - "assigned_at", - "assigned_by_username", - "id", - "user", - "username" + "method", + "received_at" ] }, - "MachineScopeOption": { + "Material": { "type": "object", "properties": { "id": { - "type": "integer" + "type": "integer", + "readOnly": true }, - "label": { - "type": "string" + "machine_type": { + "type": "integer" }, - "is_builtin": { - "type": "boolean" + "name": { + "type": "string", + "maxLength": 100 }, + "colours": {}, "is_active": { "type": "boolean" } }, "required": [ + "colours", "id", - "label" - ] - }, - "MachineServiceConsumption": { - "type": "object", - "properties": { - "makerspace_id": { - "type": "integer" - }, - "machine_id": { - "type": "integer" - }, - "machine_name": { - "type": "string" - }, - "machine_type": { - "type": "string", - "nullable": true - }, - "measurement": { - "$ref": "#/components/schemas/MachineServiceConsumptionMeasurementEnum" - }, - "product_id": { - "type": "integer", - "nullable": true - }, - "product_label": { - "type": "string" - }, - "completed_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "failed_partial_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "total_used": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - } - }, - "required": [ - "completed_amount", - "failed_partial_amount", - "machine_id", - "machine_name", "machine_type", - "measurement", - "product_id", - "product_label", - "total_used" + "name" ] }, - "MachineServiceConsumptionMeasurementEnum": { + "MeasureEnum": { + "enum": [ + "service", + "manual", + "booked" + ], + "type": "string", + "description": "* `service` - service\n* `manual` - manual\n* `booked` - booked" + }, + "Measurement883Enum": { "enum": [ "count", "grams" ], "type": "string", - "description": "* `count` - count\n* `grams` - grams" - }, - "MachineServiceFailure": { - "type": "object", - "properties": { - "makerspace_id": { - "type": "integer" - }, - "machine_id": { - "type": "integer" - }, - "machine_name": { - "type": "string" - }, - "machine_type": { - "type": "string", - "nullable": true - }, - "outcome": { - "type": "string" - }, - "failed_count": { - "type": "integer" - }, - "failed_partial_hours": { - "type": "number", - "format": "double" - }, - "failed_count_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "failed_grams_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - } - }, - "required": [ - "failed_count", - "failed_count_amount", - "failed_grams_amount", - "failed_partial_hours", - "machine_id", - "machine_name", - "machine_type", - "outcome" - ] + "description": "* `count` - Count\n* `grams` - Grams" }, - "MachineServiceMachine": { + "MemberAccountability": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, - "machine_id": { - "type": "integer" - }, - "machine_name": { - "type": "string" - }, - "machine_type": { - "type": "string", - "nullable": true - }, - "request_count": { - "type": "integer" - }, - "completed_count": { - "type": "integer" - }, - "failed_count": { - "type": "integer" - }, - "completed_hours": { - "type": "number", - "format": "double" - }, - "failed_partial_hours": { - "type": "number", - "format": "double" + "membership_active": { + "type": "boolean" }, - "total_recorded_service_hours": { - "type": "number", - "format": "double" + "waiver_acceptance_required": { + "type": "boolean" }, - "failure_rate": { - "type": "number", - "format": "double", + "restriction_code": { + "type": "string", "nullable": true } }, "required": [ - "completed_count", - "completed_hours", - "failed_count", - "failed_partial_hours", - "failure_rate", - "machine_id", - "machine_name", - "machine_type", - "request_count", - "total_recorded_service_hours" + "membership_active", + "restriction_code", + "waiver_acceptance_required" ] }, - "MachineServiceReport": { + "MemberActivity": { "type": "object", "properties": { - "status_totals": { + "active_hardware_loans": { "type": "array", "items": { - "$ref": "#/components/schemas/MachineServiceStatusTotals" + "$ref": "#/components/schemas/MemberLoanActivity" } }, - "machines": { + "print_requests": { "type": "array", "items": { - "$ref": "#/components/schemas/MachineServiceMachine" + "$ref": "#/components/schemas/MemberPrintActivity" } }, - "consumption": { + "machine_service_requests": { "type": "array", "items": { - "$ref": "#/components/schemas/MachineServiceConsumption" + "$ref": "#/components/schemas/MemberMachineServiceActivity" } }, - "failure_summary": { + "bookings": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": {} + } + }, + "event_registrations": { "type": "array", "items": { - "$ref": "#/components/schemas/MachineServiceFailure" + "$ref": "#/components/schemas/MemberEventRegistrationActivity" + } + }, + "recent_presence_sessions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberPresenceActivity" + } + }, + "currently_checked_in": { + "type": "boolean" + }, + "accountability": { + "$ref": "#/components/schemas/MemberAccountability" + }, + "loan_history": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberLoanHistory" + } + }, + "request_history": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberRequestHistory" + } + }, + "membership_dues": { + "$ref": "#/components/schemas/MemberDues" + }, + "membership_term": { + "allOf": [ + { + "$ref": "#/components/schemas/MemberTerm" + } + ], + "nullable": true + }, + "notices": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberNotice" } } }, "required": [ - "consumption", - "failure_summary", - "machines", - "status_totals" + "accountability", + "active_hardware_loans", + "currently_checked_in", + "recent_presence_sessions" ] }, - "MachineServiceReportResponse": { - "oneOf": [ - { - "$ref": "#/components/schemas/MachineServiceReport" + "MemberActivityReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - { - "$ref": "#/components/schemas/PrinterServiceReport" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberActivityRow" + } } + }, + "required": [ + "rows", + "typed_rows" ] }, - "MachineServiceRequest": { + "MemberActivityRow": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "makerspace_id": { + "type": "integer" }, - "bucket_id": { - "type": "integer", - "readOnly": true + "makerspace_name": { + "type": "string" }, - "queue_id": { + "membership_policy": { + "type": "string" + }, + "referrals_enabled": { + "type": "boolean" + }, + "new_members": { "type": "integer", - "readOnly": true + "description": "Current activated_at values in the selected [start, end) range, not event history." }, - "machine_type": { - "type": "string", - "readOnly": true + "active_members": { + "type": "integer", + "description": "Current membership snapshot." }, - "machine": { - "allOf": [ - { - "$ref": "#/components/schemas/ServiceMachine" - } - ], - "readOnly": true + "revoked_members": { + "type": "integer", + "description": "Current revoked_at values in the selected [start, end) range, not event history." }, - "assigned_machine": { - "allOf": [ - { - "$ref": "#/components/schemas/ServiceMachine" - } - ], - "readOnly": true + "pending_requests": { + "type": "integer", + "description": "Current membership-request snapshot." }, - "requester": { - "allOf": [ - { - "$ref": "#/components/schemas/ServiceRequester" - } - ], - "readOnly": true + "open_invites": { + "type": "integer", + "description": "Current membership-request snapshot." }, - "requester_name": { - "type": "string", - "readOnly": true + "referred_joins": { + "type": "integer", + "description": "Active automatic referrals with decided_at in the selected [start, end) range." }, - "contact_email": { + "verified_members": { + "type": "integer", + "description": "Current verified_at snapshot." + } + }, + "required": [ + "active_members", + "makerspace_name", + "membership_policy", + "new_members", + "open_invites", + "pending_requests", + "referrals_enabled", + "referred_joins", + "revoked_members", + "verified_members" + ] + }, + "MemberBooking": { + "type": "object", + "properties": { + "public_token": { "type": "string", + "format": "uuid", "readOnly": true }, - "contact_phone": { + "space_name": { "type": "string", "readOnly": true }, - "title": { + "timezone": { "type": "string", "readOnly": true }, - "description": { + "starts_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "source_link": { + "ends_at": { "type": "string", - "format": "uri", + "format": "date-time", "readOnly": true }, "status": { "allOf": [ { - "$ref": "#/components/schemas/MachineServiceRequestStatusEnum" + "$ref": "#/components/schemas/Status2caEnum" } ], "readOnly": true }, - "reason": { + "cancellation_cutoff_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "estimated_minutes": { - "type": "integer", - "readOnly": true - }, - "actual_minutes": { - "type": "integer", + "can_cancel": { + "type": "boolean", "readOnly": true }, - "fail_percent_complete": { - "type": "integer", + "confirmation_available": { + "type": "boolean", "readOnly": true + } + }, + "required": [ + "can_cancel", + "cancellation_cutoff_at", + "confirmation_available", + "ends_at", + "public_token", + "space_name", + "starts_at", + "status", + "timezone" + ] + }, + "MemberBookingList": { + "type": "object", + "properties": { + "count": { + "type": "integer" }, - "accepted_at": { + "next": { "type": "string", - "format": "date-time", - "readOnly": true, + "format": "uri", "nullable": true }, - "started_at": { + "previous": { "type": "string", - "format": "date-time", - "readOnly": true, + "format": "uri", "nullable": true }, - "completed_at": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberBooking" + } + } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] + }, + "MemberBookingUsage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "monthly": { + "$ref": "#/components/schemas/MemberUsageSeries" + } + }, + "required": [ + "count", + "monthly" + ] + }, + "MemberCalendarFeedIssue": { + "type": "object", + "properties": { + "confirm_bearer_risk": { + "type": "boolean" + } + }, + "required": [ + "confirm_bearer_risk" + ] + }, + "MemberCalendarFeedIssued": { + "type": "object", + "properties": { + "feed_url": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "token_hint": { + "type": "string", + "readOnly": true + }, + "created_at": { "type": "string", "format": "date-time", + "readOnly": true + } + }, + "required": [ + "created_at", + "feed_url", + "token_hint" + ] + }, + "MemberCalendarFeedState": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "readOnly": true + }, + "token_hint": { + "type": "string", "readOnly": true, "nullable": true }, - "failed_at": { + "created_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "collected_at": { + "rotated_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true + } + }, + "required": [ + "created_at", + "enabled", + "rotated_at", + "token_hint" + ] + }, + "MemberCard": { + "type": "object", + "description": "Never carries the photo key or a URL that outlives the response.", + "properties": { + "id": { + "type": "integer", + "readOnly": true }, - "created_at": { - "type": "string", - "format": "date-time", + "card_number": { + "type": "integer", "readOnly": true }, - "updated_at": { + "printed_name": { "type": "string", - "format": "date-time", "readOnly": true }, - "capability_payload": { + "membership_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "is_active": { + "type": "boolean", "readOnly": true }, - "metering_unit": { + "photo_set": { + "type": "boolean", + "readOnly": true + }, + "photo_consent_at": { "type": "string", + "format": "date-time", "readOnly": true, "nullable": true }, - "planned_quantity": { + "qr_active": { + "type": "boolean", + "readOnly": true + }, + "print_count": { + "type": "integer", + "readOnly": true + }, + "last_printed_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "format": "date-time", "readOnly": true, "nullable": true }, - "reserved_quantity": { + "issued_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true, - "nullable": true + "format": "date-time", + "readOnly": true }, - "actual_consumed_quantity": { + "revoked_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "format": "date-time", "readOnly": true, "nullable": true }, - "planned_grams": { + "revoked_reason": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "reserved_grams": { + "created_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "format": "date-time", "readOnly": true }, - "actual_consumed_grams": { + "updated_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true - }, - "payment": { - "allOf": [ - { - "$ref": "#/components/schemas/StaffPayment" - } - ], - "nullable": true, + "format": "date-time", "readOnly": true + } + }, + "required": [ + "card_number", + "created_at", + "id", + "is_active", + "issued_at", + "last_printed_at", + "membership_id", + "photo_consent_at", + "photo_set", + "print_count", + "printed_name", + "qr_active", + "revoked_at", + "revoked_reason", + "updated_at" + ] + }, + "MemberCardIssue": { + "type": "object", + "properties": { + "printed_name": { + "type": "string", + "maxLength": 200 + } + } + }, + "MemberCardPhotoFinalize": { + "type": "object", + "properties": { + "object_key": { + "type": "string", + "maxLength": 300 }, - "run_machine_model": { + "content_type": { "type": "string", - "readOnly": true + "maxLength": 64 }, - "files": { + "consent": { + "type": "boolean" + } + }, + "required": [ + "consent", + "content_type", + "object_key" + ] + }, + "MemberCardPhotoPresign": { + "type": "object", + "properties": { + "content_type": { + "type": "string", + "maxLength": 64 + } + }, + "required": [ + "content_type" + ] + }, + "MemberCardPrint": { + "type": "object", + "properties": { + "card_ids": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceFile" - }, - "readOnly": true + "type": "integer" + } }, - "consumptions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ServiceConsumption" - }, - "readOnly": true + "preset": { + "allOf": [ + { + "$ref": "#/components/schemas/PresetEnum" + } + ], + "default": "sheet" + } + } + }, + "MemberCardReissue": { + "type": "object", + "properties": { + "reason": { + "$ref": "#/components/schemas/MemberCardReissueReasonEnum" } }, "required": [ - "accepted_at", - "actual_consumed_grams", - "actual_consumed_quantity", - "actual_minutes", - "assigned_machine", - "bucket_id", - "capability_payload", - "collected_at", - "completed_at", - "consumptions", - "contact_email", - "contact_phone", - "created_at", - "description", - "estimated_minutes", - "fail_percent_complete", - "failed_at", - "files", - "id", - "machine", - "machine_type", - "metering_unit", - "payment", - "planned_grams", - "planned_quantity", - "queue_id", - "reason", - "requester", - "requester_name", - "reserved_grams", - "reserved_quantity", - "run_machine_model", - "source_link", - "started_at", - "status", - "title", - "updated_at" + "reason" ] }, - "MachineServiceRequestStatusEnum": { + "MemberCardReissueReasonEnum": { "enum": [ - "pending", - "accepted", - "in_progress", - "completed", - "collected", - "rejected", - "failed" + "lost", + "stolen", + "damaged", + "renewed" ], "type": "string", - "description": "* `pending` - Pending\n* `accepted` - Accepted\n* `in_progress` - In progress\n* `completed` - Completed\n* `collected` - Collected\n* `rejected` - Rejected\n* `failed` - Failed" + "description": "* `lost` - lost\n* `stolen` - stolen\n* `damaged` - damaged\n* `renewed` - renewed" }, - "MachineServiceStatusTotals": { + "MemberCardResolve": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, - "submitted": { - "type": "integer" - }, - "accepted": { - "type": "integer" - }, - "in_progress": { - "type": "integer" - }, - "completed": { - "type": "integer" - }, - "collected": { - "type": "integer" - }, - "rejected": { - "type": "integer" - }, - "failed": { - "type": "integer" + "payload": { + "type": "string", + "maxLength": 64 } }, "required": [ - "accepted", - "collected", - "completed", - "failed", - "in_progress", - "rejected", - "submitted" + "payload" ] }, - "MachineServiceSubmit": { + "MemberCardResolveResult": { "type": "object", "properties": { - "requester_id": { - "type": "integer", - "minimum": 1 + "outcome": { + "$ref": "#/components/schemas/MemberCardResolveResultOutcomeEnum" }, - "machine_id": { - "type": "integer", - "minimum": 1 + "card_id": { + "type": "integer" }, - "title": { - "type": "string", - "maxLength": 200 + "card_number": { + "type": "integer" }, - "description": { + "printed_name": { "type": "string" }, - "source_link": { - "oneOf": [ - { - "type": "string", - "format": "uri" - }, - { - "type": "string", - "maxLength": 0 - } - ] + "membership_id": { + "type": "integer" }, - "requester_name": { + "membership_status": { "type": "string" }, - "contact_email": { - "oneOf": [ - { - "type": "string", - "format": "email" - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "contact_phone": { + "photo_url": { "type": "string", - "maxLength": 32 - }, - "capability_payload": {} + "nullable": true + } }, "required": [ - "machine_id", - "requester_id", - "title" + "outcome" ] }, - "MachineStatusEnum": { + "MemberCardResolveResultOutcomeEnum": { "enum": [ - "idle", - "running", - "reserved", - "maintenance", - "offline" + "ok", + "revoked", + "inactive" ], "type": "string", - "description": "* `idle` - Idle\n* `running` - Running\n* `reserved` - Reserved\n* `maintenance` - Maintenance\n* `offline` - Offline" + "description": "* `ok` - ok\n* `revoked` - revoked\n* `inactive` - inactive" }, - "MachineType": { + "MemberCardRevoke": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "slug": { + "reason": { "type": "string", - "maxLength": 50, - "pattern": "^[-a-zA-Z0-9_]+$" + "maxLength": 32 + } + } + }, + "MemberCardTemplate": { + "type": "object", + "properties": { + "version": { + "type": "integer" }, - "name": { - "type": "string", - "maxLength": 200 + "page": { + "type": "string" }, - "icon": { - "type": "string", - "maxLength": 50 + "orientation": { + "type": "string" }, - "is_builtin": { + "card_width_mm": { + "type": "number", + "format": "double" + }, + "card_height_mm": { + "type": "number", + "format": "double" + }, + "margin_mm": { + "type": "number", + "format": "double" + }, + "gap_mm": { + "type": "number", + "format": "double" + }, + "front_fields": { + "type": "array", + "items": { + "type": "string" + } + }, + "back_text": { + "type": "string" + }, + "include_photo": { "type": "boolean" }, - "managing_action": { - "type": "string", - "readOnly": true + "include_qr": { + "type": "boolean" }, - "capability_config": { - "readOnly": true + "name_font_size_pt": { + "type": "integer" }, - "makerspace": { - "type": "integer", - "readOnly": true, - "nullable": true + "font_size_pt": { + "type": "integer" + }, + "crop_marks": { + "type": "boolean" } - }, - "required": [ - "capability_config", - "id", - "makerspace", - "managing_action", - "name", - "slug" - ] + } }, - "MachineTypeAccess": { + "MemberCertification": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "slug": { - "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" - }, "name": { - "type": "string", - "readOnly": true + "type": "string" }, - "icon": { - "type": "string", - "readOnly": true + "machine_type_name": { + "type": "string" }, - "is_builtin": { - "type": "boolean", - "readOnly": true + "type_active": { + "type": "boolean" }, - "managing_action": { + "granted_at": { "type": "string", - "readOnly": true + "format": "date-time" }, - "capability_config": { - "readOnly": true + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "makerspace": { - "type": "integer", - "readOnly": true, + "revoked_at": { + "type": "string", + "format": "date-time", "nullable": true }, - "can_create_machine": { + "is_live": { "type": "boolean", "readOnly": true } }, "required": [ - "can_create_machine", - "capability_config", - "icon", "id", - "is_builtin", - "makerspace", - "managing_action", + "is_live", + "machine_type_name", "name", - "slug" + "type_active" ] }, - "MachineTypeCreate": { + "MemberCertificationPage": { "type": "object", "properties": { - "slug": { - "type": "string", - "maxLength": 50, - "pattern": "^[-a-zA-Z0-9_]+$" + "count": { + "type": "integer" }, - "name": { + "next": { "type": "string", - "maxLength": 200 + "nullable": true }, - "icon": { + "previous": { "type": "string", - "maxLength": 50 + "nullable": true }, - "capability_config": {} + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberCertification" + } + } }, "required": [ - "name", - "slug" + "count", + "next", + "previous", + "results" ] }, - "MachineTypeOption": { + "MemberClaimCode": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "name": { + "membership_id": { + "type": "integer", + "readOnly": true + }, + "member_display_name": { "type": "string", "readOnly": true }, - "is_active": { - "type": "boolean", + "issued_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "issued_at": { + "type": "string", + "format": "date-time", "readOnly": true }, - "is_overridden": { - "type": "boolean", + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "consumed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "status": { + "type": "string", "readOnly": true } }, "required": [ + "consumed_at", + "expires_at", "id", - "is_active", - "is_overridden", - "name" + "issued_at", + "issued_by_id", + "member_display_name", + "membership_id", + "revoked_at", + "status" ] }, - "MachineTypePolicy": { + "MemberClaimCodeIssueRequest": { "type": "object", "properties": { - "cert_reminder_days": { - "type": "integer", - "maximum": 365, - "minimum": 0, - "nullable": true - }, - "reminder_lead_days": { - "type": "integer", - "maximum": 365, - "minimum": 0, - "nullable": true - }, - "grace_days": { + "membership_id": { "type": "integer", - "maximum": 365, - "minimum": 0, - "nullable": true + "minimum": 1 } - } + }, + "required": [ + "membership_id" + ] }, - "MachineTypePricing": { + "MemberClaimCodeIssueResponse": { "type": "object", "properties": { - "machine_type_id": { + "id": { "type": "integer", "readOnly": true }, - "rate_per_unit": { + "membership_id": { + "type": "integer", + "readOnly": true + }, + "member_display_name": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "flat_fee": { + "issued_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "issued_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "format": "date-time", "readOnly": true }, - "payment_enabled": { - "type": "boolean", + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "consumed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "status": { + "type": "string", + "readOnly": true + }, + "code": { + "type": "string", + "readOnly": true + }, + "qr_svg": { + "type": "string", "readOnly": true } }, "required": [ - "flat_fee", - "machine_type_id", - "payment_enabled", - "rate_per_unit" + "code", + "consumed_at", + "expires_at", + "id", + "issued_at", + "issued_by_id", + "member_display_name", + "membership_id", + "qr_svg", + "revoked_at", + "status" ] }, - "MachineTypePricingList": { + "MemberDues": { "type": "object", "properties": { - "currency": { - "type": "string", - "readOnly": true + "dues_amount": { + "type": "string" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineTypePricing" + "outstanding_by_currency": { + "type": "object", + "additionalProperties": { + "type": "string" }, - "readOnly": true + "nullable": true } }, "required": [ - "currency", - "results" + "dues_amount", + "outstanding_by_currency" ] }, - "MachineTypePricingSet": { + "MemberEventRegistrationActivity": { "type": "object", "properties": { - "rate_per_unit": { + "registration_id": { + "type": "integer" + }, + "checkin_token": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "uuid", + "nullable": true }, - "flat_fee": { + "event_title": { + "type": "string" + }, + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string" + }, + "cancellation_cutoff_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "date-time" }, - "payment_enabled": { + "can_cancel": { + "type": "boolean" + }, + "waitlist_position": { + "type": "integer", + "nullable": true + }, + "feedback_available": { "type": "boolean" + }, + "feedback_path": { + "type": "string", + "nullable": true + }, + "certificate": { + "type": "object", + "additionalProperties": {}, + "nullable": true } }, "required": [ - "flat_fee", - "payment_enabled", - "rate_per_unit" + "can_cancel", + "cancellation_cutoff_at", + "certificate", + "checkin_token", + "ends_at", + "event_title", + "feedback_available", + "feedback_path", + "registration_id", + "starts_at", + "status", + "waitlist_position" ] }, - "MachineUsageEntry": { + "MemberEventTicket": { "type": "object", "properties": { - "id": { + "registration_id": { "type": "integer", "readOnly": true }, - "hours": { + "status": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", "readOnly": true }, - "source": { + "event": { "allOf": [ { - "$ref": "#/components/schemas/MachineUsageEntrySourceEnum" + "$ref": "#/components/schemas/TicketPublicFields" } ], "readOnly": true }, - "note": { - "type": "string", - "readOnly": true - }, - "logged_by_username": { - "type": "string", - "readOnly": true, - "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time", + "ticket_png_available": { + "type": "boolean", "readOnly": true } }, "required": [ - "created_at", - "hours", - "id", - "logged_by_username", - "note", - "source" - ] - }, - "MachineUsageEntrySourceEnum": { - "enum": [ - "manual", - "typed_manual" - ], - "type": "string", - "description": "* `manual` - Manual hours\n* `typed_manual` - Typed manual service" - }, - "MachineUsageReport": { - "type": "object", - "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineUsageRow" - } - } - }, - "required": [ - "rows", - "typed_rows" + "event", + "registration_id", + "status", + "ticket_png_available" ] }, - "MachineUsageRow": { + "MemberEventUsage": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, - "machine_id": { - "type": "integer" - }, - "machine_name": { - "type": "string" - }, - "machine_type": { - "type": "string" - }, - "is_active": { - "type": "boolean" - }, - "usage_entries": { + "attended_count": { "type": "integer" }, - "usage_hours": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "chart": { + "$ref": "#/components/schemas/MemberUsageSeries" } }, "required": [ - "is_active", - "machine_id", - "machine_name", - "machine_type", - "usage_entries", - "usage_hours" + "attended_count", + "chart" ] }, - "MaintenanceActivityReport": { + "MemberKitPreviewInput": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "payload": { + "type": "string", + "maxLength": 64 }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MaintenanceActivityRow" - } + "phase": { + "$ref": "#/components/schemas/PhaseEnum" } }, "required": [ - "rows", - "typed_rows" + "payload", + "phase" ] }, - "MaintenanceActivityRow": { + "MemberLoanActivity": { "type": "object", "properties": { - "makerspace_id": { + "request_id": { "type": "integer" }, - "machine_id": { + "renewal_count": { "type": "integer" }, - "machine_name": { - "type": "string" - }, - "machine_type": { - "type": "string" - }, - "is_active": { + "can_renew": { "type": "boolean" }, - "log_count": { - "type": "integer" - }, - "costed_log_count": { - "type": "integer" - }, - "total_cost": { + "renewal_refusal": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "nullable": true }, - "average_cost": { + "label": { + "type": "string" + }, + "checked_out_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", - "nullable": true + "format": "date-time" }, - "last_performed_at": { + "due_at": { "type": "string", "format": "date-time", "nullable": true }, - "average_interval_days": { - "type": "number", - "format": "double", - "nullable": true - }, - "active_schedules": { - "type": "integer" - }, - "overdue_schedules": { - "type": "integer" + "overdue": { + "type": "boolean" } }, "required": [ - "active_schedules", - "average_cost", - "average_interval_days", - "costed_log_count", - "is_active", - "last_performed_at", - "log_count", - "machine_id", - "machine_name", - "machine_type", - "overdue_schedules", - "total_cost" + "can_renew", + "checked_out_at", + "due_at", + "label", + "overdue", + "renewal_count", + "renewal_refusal", + "request_id" ] }, - "MaintenanceDocumentFinalize": { + "MemberLoanHistory": { "type": "object", "properties": { - "object_key": { + "label": { + "type": "string" + }, + "checked_out_at": { "type": "string", - "maxLength": 500 - } - }, - "required": [ - "object_key" - ] - }, - "MaintenanceDocumentPresign": { - "type": "object", - "properties": { - "filename": { + "format": "date-time" + }, + "returned_at": { "type": "string", - "maxLength": 255 + "format": "date-time", + "nullable": true }, - "content_type": { + "due_at": { "type": "string", - "maxLength": 100 + "format": "date-time", + "nullable": true + }, + "returned_late": { + "type": "boolean" } }, "required": [ - "content_type", - "filename" + "checked_out_at", + "due_at", + "label", + "returned_at", + "returned_late" ] }, - "MaintenanceDocumentPresignResponse": { + "MemberLoanUsage": { "type": "object", "properties": { - "object_key": { - "type": "string" + "count": { + "type": "integer" }, - "upload": { - "$ref": "#/components/schemas/MaintenanceDocumentUpload" + "returned_count": { + "type": "integer" + }, + "currently_out": { + "type": "integer" + }, + "monthly": { + "$ref": "#/components/schemas/MemberUsageSeries" } }, "required": [ - "object_key", - "upload" + "count", + "currently_out", + "monthly", + "returned_count" ] }, - "MaintenanceDocumentUpload": { + "MemberMachineScope": { "type": "object", "properties": { - "url": { - "type": "string", - "format": "uri" + "membership_id": { + "type": "integer" }, - "method": { - "type": "string" + "scoping_applies": { + "type": "boolean" }, - "fields": { - "type": "object", - "additionalProperties": {} + "machine_type_ids": { + "type": "array", + "items": { + "type": "integer" + } }, - "headers": { - "type": "object", - "additionalProperties": {} + "available_machine_types": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AssignableMachineType" + } } }, "required": [ - "url" + "available_machine_types", + "machine_type_ids", + "membership_id", + "scoping_applies" ] }, - "MaintenanceDocumentUrl": { + "MemberMachineScopeWrite": { "type": "object", "properties": { - "url": { - "type": "string", - "format": "uri" + "machine_type_ids": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1 + } } }, "required": [ - "url" + "machine_type_ids" ] }, - "MaintenanceLog": { + "MemberMachineServiceActivity": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "machine_id": { - "type": "integer", - "readOnly": true - }, - "performed_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "performed_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "summary": { - "type": "string", - "readOnly": true + "machine_type": { + "type": "string" }, - "cost": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true, - "nullable": true + "title": { + "type": "string" }, - "parts_note": { - "type": "string", - "readOnly": true + "status": { + "type": "string" }, "created_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" }, - "documents": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MaintenanceLogDocument" - }, - "readOnly": true + "queue_position": { + "type": "integer", + "nullable": true } }, "required": [ - "cost", "created_at", - "documents", - "id", - "machine_id", - "parts_note", - "performed_at", - "performed_by_id", - "summary" + "queue_position", + "status", + "title" ] }, - "MaintenanceLogDocument": { + "MemberMachineUsage": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "log_id": { - "type": "integer", - "readOnly": true - }, - "object_key": { - "type": "string", - "readOnly": true - }, - "size_bytes": { - "type": "integer", - "readOnly": true + "hours": { + "type": "string" }, - "uploaded_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "monthly": { + "$ref": "#/components/schemas/MemberUsageSeries" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "top_machines": { + "$ref": "#/components/schemas/MemberUsageSeries" } }, "required": [ - "created_at", - "id", - "log_id", - "object_key", - "size_bytes", - "uploaded_by_id" + "hours", + "monthly", + "top_machines" ] }, - "MaintenanceLogList": { + "MemberMessaging": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true + "sms": { + "$ref": "#/components/schemas/MessagingChannel" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MaintenanceLog" - } + "whatsapp": { + "$ref": "#/components/schemas/MessagingChannel" } }, "required": [ - "count", - "results" + "sms", + "whatsapp" ] }, - "MaintenanceLogWrite": { + "MemberNotice": { "type": "object", + "description": "A fact about the reader, derived from their own rows.\n\nNot `notifications.Notification`: that table is makerspace-wide, has no recipient\nand shares one `read_at`, so serving it would leak staff alerts.", "properties": { - "summary": { + "level": { "type": "string" }, - "performed_at": { - "type": "string", - "format": "date-time" - }, - "cost": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "nullable": true - }, - "parts_note": { - "type": "string", - "default": "" + "event": { + "type": "string" }, - "set_idle": { - "type": "boolean", - "default": false + "title": { + "type": "string" }, - "schedule_id": { - "type": "integer", - "minimum": 1, - "writeOnly": true + "body": { + "type": "string" } }, "required": [ - "summary" + "body", + "event", + "level", + "title" ] }, - "MaintenanceSchedule": { + "MemberPayment": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "machine_id": { - "type": "integer", - "readOnly": true + "subject_type": { + "$ref": "#/components/schemas/SubjectTypeEnum" }, - "description": { + "subject_label": { "type": "string", "readOnly": true }, - "interval_days": { - "type": "integer", - "readOnly": true + "status": { + "$ref": "#/components/schemas/Status66aEnum" }, - "next_due": { + "amount": { "type": "string", - "format": "date", - "readOnly": true - }, - "anchor": { - "allOf": [ - { - "$ref": "#/components/schemas/AnchorEnum" - } - ], - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "trigger": { - "allOf": [ - { - "$ref": "#/components/schemas/MaintenanceScheduleTriggerEnum" - } - ], - "readOnly": true + "currency": { + "type": "string", + "pattern": "^[a-z]{3}$", + "maxLength": 3 }, - "usage_hours": { + "due_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true, + "format": "date-time", "nullable": true }, - "job_count": { - "type": "integer", - "readOnly": true, - "nullable": true + "due_date": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "due_state": { + "type": "string", + "readOnly": true }, - "meter_started_at": { + "checkout_url": { "type": "string", - "format": "date-time", "readOnly": true }, - "is_active": { + "online_payment_available": { "type": "boolean", + "description": "Whether offering to pay online is honest for this charge.\n\nResolved once per makerspace by the view and passed in: doing it per row would\nre-read settings and credentials for every charge in the list. Falls back to a\nlive check only when a caller has not supplied it.", "readOnly": true }, - "created_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "settlement": { + "allOf": [ + { + "$ref": "#/components/schemas/MemberSettlement" + } + ], + "nullable": true, + "readOnly": true }, - "created_at": { - "type": "string", - "format": "date-time", + "refunded_amount": { + "type": "number", + "format": "double", "readOnly": true }, - "updated_at": { - "type": "string", - "format": "date-time", + "refunds": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemberRefund" + }, "readOnly": true }, - "overdue": { - "type": "boolean", + "created_at": { + "type": "string", + "format": "date-time", "readOnly": true } }, "required": [ - "anchor", + "amount", + "checkout_url", "created_at", - "created_by_id", - "description", + "currency", + "due_date", + "due_state", "id", - "interval_days", - "is_active", - "job_count", - "machine_id", - "meter_started_at", - "next_due", - "overdue", - "trigger", - "updated_at", - "usage_hours" + "online_payment_available", + "refunded_amount", + "refunds", + "settlement", + "subject_label", + "subject_type" ] }, - "MaintenanceScheduleList": { + "MemberPaymentUsage": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { - "type": "string", - "nullable": true - }, - "previous": { - "type": "string", - "nullable": true + "paid_by_currency": { + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MaintenanceSchedule" + "outstanding_by_currency": { + "type": "object", + "additionalProperties": { + "type": "string" } + }, + "totals": { + "$ref": "#/components/schemas/MemberUsageSeries" } }, "required": [ - "count", - "results" + "outstanding_by_currency", + "paid_by_currency", + "totals" ] }, - "MaintenanceScheduleTriggerEnum": { - "enum": [ - "days", - "usage", - "both" - ], - "type": "string", - "description": "* `days` - Days\n* `usage` - Usage\n* `both` - Days or usage" - }, - "MaintenanceScheduleWrite": { + "MemberPresenceActivity": { "type": "object", "properties": { - "description": { - "type": "string" - }, - "interval_days": { - "type": "integer", - "minimum": 1 - }, - "next_due": { + "started_at": { "type": "string", - "format": "date" + "format": "date-time" }, - "trigger": { - "$ref": "#/components/schemas/MaintenanceScheduleWriteTriggerEnum" + "expires_at": { + "type": "string", + "format": "date-time" }, - "usage_hours": { + "ended_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "format": "date-time", "nullable": true }, - "job_count": { - "type": "integer", - "minimum": 1, - "nullable": true + "end_reason": { + "type": "string" }, - "anchor": { - "$ref": "#/components/schemas/AnchorEnum" + "active": { + "type": "boolean" } }, "required": [ - "description", - "interval_days", - "next_due" + "active", + "end_reason", + "ended_at", + "expires_at", + "started_at" ] }, - "MaintenanceScheduleWriteTriggerEnum": { - "enum": [ - "days", - "usage", - "both" - ], - "type": "string", - "description": "* `days` - days\n* `usage` - usage\n* `both` - both" - }, - "Makerspace": { + "MemberPrintActivity": { "type": "object", "properties": { - "creation_setup": { - "allOf": [ - { - "$ref": "#/components/schemas/MakerspaceCreationSetup" - } - ], - "writeOnly": true - }, - "creation_invitation": { - "allOf": [ - { - "$ref": "#/components/schemas/CreationInviteDelivery" - } - ], - "readOnly": true, - "nullable": true - }, - "install_profile_id": { - "type": "integer", - "minimum": 1, - "writeOnly": true - }, - "id": { - "type": "integer", - "readOnly": true - }, - "name": { - "type": "string", - "maxLength": 200 - }, - "public_code": { + "public_token": { "type": "string", - "pattern": "^[A-Z0-9]{4}$", - "maxLength": 4 + "format": "uuid" }, - "slug": { - "type": "string", - "maxLength": 50, - "pattern": "^[-a-zA-Z0-9_]+$" + "status": { + "type": "string" }, - "location": { - "type": "string", - "maxLength": 200 + "title": { + "type": "string" }, - "timezone": { + "created_at": { "type": "string", - "readOnly": true + "format": "date-time" }, - "country": { + "accepted_at": { "type": "string", - "readOnly": true - }, - "opening_hours": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "required": [ - "weekly", - "exceptions" - ], - "additionalProperties": false, - "properties": { - "weekly": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "additionalProperties": false, - "required": [ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun" - ], - "properties": { - "mon": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "tue": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "wed": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "thu": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "fri": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sat": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sun": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - } - } - } - ] - }, - "exceptions": { - "type": "object", - "maxProperties": 366, - "additionalProperties": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." - } - } - } - ], - "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days.", - "readOnly": true - }, - "map_url": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "format": "date-time", + "nullable": true }, - "geofence_latitude": { + "started_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,3}(?:\\.\\d{0,6})?$", + "format": "date-time", "nullable": true }, - "geofence_longitude": { + "completed_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,3}(?:\\.\\d{0,6})?$", + "format": "date-time", "nullable": true }, - "geofence_radius_m": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1 - }, - "geofence_enabled": { - "type": "boolean" - }, - "public_inventory_enabled": { - "type": "boolean" - }, - "public_site_enabled": { - "type": "boolean" - }, - "public_landing_page": { - "$ref": "#/components/schemas/PublicLandingPage6a0Enum" - }, - "public_stats_enabled": { - "type": "boolean" - }, - "public_stats_show_holder_names": { - "type": "boolean" + "estimated_minutes": { + "type": "integer" }, - "public_print_status_lookup_policy": { - "$ref": "#/components/schemas/PublicPrintStatusLookupPolicyEnum" + "queue_position": { + "type": "integer", + "nullable": true }, - "membership_policy": { - "$ref": "#/components/schemas/MembershipPolicyEnum" + "queue_approved_ahead": { + "type": "integer", + "nullable": true }, - "membership_dues_amount": { + "queue_awaiting_review_ahead": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "accepted_at", + "completed_at", + "created_at", + "estimated_minutes", + "public_token", + "queue_approved_ahead", + "queue_awaiting_review_ahead", + "queue_position", + "started_at", + "status", + "title" + ] + }, + "MemberRefund": { + "type": "object", + "description": "Money sent back on the member's own charge. Never names the staff member.", + "properties": { + "amount": { "type": "string", "format": "decimal", "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "referrals_enabled": { - "type": "boolean" + "currency": { + "type": "string" }, - "lapsed_members_cannot_borrow": { - "type": "boolean" + "status": { + "type": "string" }, - "anonymous_requests_enabled": { - "type": "boolean" + "reason": { + "type": "string" }, - "request_access": { + "settled_at": { "type": "string", - "readOnly": true + "format": "date-time", + "nullable": true }, - "filament_low_stock_threshold_grams": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$" + "method": { + "type": "string" }, - "superadmin_access_enabled": { - "type": "boolean" + "reference": { + "type": "string" }, - "archive_custody_state": { + "refunded_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "required": [ + "amount", + "currency", + "method", + "reason", + "reference", + "refunded_at", + "settled_at", + "status" + ] + }, + "MemberRegistrationCancelResponse": { + "type": "object", + "properties": { + "status": { "allOf": [ { - "$ref": "#/components/schemas/ArchiveCustodyStateEnum" + "$ref": "#/components/schemas/MemberRegistrationCancelResponseStatusEnum" } ], - "nullable": true, "readOnly": true + } + }, + "required": [ + "status" + ] + }, + "MemberRegistrationCancelResponseStatusEnum": { + "enum": [ + "cancelled" + ], + "type": "string", + "description": "* `cancelled` - cancelled" + }, + "MemberRequestHistory": { + "type": "object", + "properties": { + "status": { + "type": "string" }, - "staff_notifications_enabled": { - "type": "boolean" + "created_at": { + "type": "string", + "format": "date-time" }, - "booking_requester_notifications_enabled": { - "type": "boolean" + "item_count": { + "type": "integer" }, - "event_default_registration_audience": { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + "returned_quantity": { + "type": "integer" }, - "event_reminder_lead_hours": { + "damaged_quantity": { + "type": "integer" + }, + "missing_quantity": { + "type": "integer" + } + }, + "required": [ + "created_at", + "damaged_quantity", + "item_count", + "missing_quantity", + "returned_quantity", + "status" + ] + }, + "MemberRetentionReport": { + "type": "object", + "properties": { + "rows": { "type": "array", "items": { - "type": "integer", - "maximum": 8760, - "minimum": 1 - }, - "maxItems": 100 + "type": "array", + "items": {} + } }, - "booking_reminder_lead_hours": { + "typed_rows": { "type": "array", "items": { - "type": "integer", - "maximum": 8760, - "minimum": 1 - }, - "maxItems": 100 + "$ref": "#/components/schemas/MemberRetentionRow" + } }, - "logo_key": { - "type": "string", - "readOnly": true + "meta": { + "type": "object", + "additionalProperties": {} }, - "logo_url": { - "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true + "report_key": { + "type": "string" + } + }, + "required": [ + "meta", + "report_key", + "rows", + "typed_rows" + ] + }, + "MemberRetentionRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "cover_image_key": { - "type": "string", - "readOnly": true + "cohort_month": { + "type": "string" }, - "cover_image_url": { - "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true + "months_after_join": { + "type": "integer" }, - "frontend_domain": { - "type": "string", - "nullable": true, - "maxLength": 255 + "cohort_members": { + "type": "integer" }, - "frontend_domain_status": { - "allOf": [ - { - "$ref": "#/components/schemas/FrontendDomainStatusEnum" - } - ], - "readOnly": true + "eligible_members": { + "type": "integer" }, - "domain_verified_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "retained_members": { + "type": "integer" }, - "domain_verification_token": { + "retention_percent": { "type": "string", - "readOnly": true - }, - "domain_verification_record": { - "type": "object", - "nullable": true, - "properties": { - "host": { - "type": "string" - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "readOnly": true - }, - "platform_hosting": { - "type": "boolean", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", + "nullable": true }, - "is_platform_subdomain": { - "type": "boolean", - "readOnly": true + "churned_members": { + "type": "integer" }, - "hidden_from_central_directory": { - "type": "boolean" + "reactivated_members": { + "type": "integer" + } + }, + "required": [ + "churned_members", + "cohort_members", + "cohort_month", + "eligible_members", + "months_after_join", + "reactivated_members", + "retained_members", + "retention_percent" + ] + }, + "MemberSettlement": { + "type": "object", + "description": "The receipt for a charge staff settled in person, shown back to the payer.", + "properties": { + "method": { + "type": "string" }, - "public_api_key": { + "received_at": { "type": "string", - "readOnly": true - }, - "cors_allowed_origins": { - "readOnly": true - }, - "enabled_modules": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true - }, - "unavailable_apps": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true - }, - "resource_limit_overrides": {}, - "enabled_features": {}, - "theme_config": { - "type": "object", - "additionalProperties": false, - "description": "Public/member secondary colours. Foreground, hover/pressed states and light/dark link ink are derived automatically. Rejects palettes below 4.5:1 text or 3:1 component contrast in either mode (including 80% counter text and tinted surfaces). Blank colours use the product palette. Staff chrome and status colours stay fixed.", - "properties": { - "mode": { - "type": "string", - "enum": [ - "system", - "light", - "dark" - ] - }, - "primary_color": { - "type": "string", - "pattern": "^$|^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" - }, - "accent_color": { - "type": "string", - "pattern": "^$|^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" - }, - "logo_url": { - "type": "string", - "maxLength": 2048 - } - } - }, - "branding_config": { - "readOnly": true + "format": "date-time" }, - "public_display_name": { + "reference": { + "type": "string" + } + }, + "required": [ + "method", + "received_at", + "reference" + ] + }, + "MemberSignUp": { + "type": "object", + "properties": { + "display_name": { "type": "string", - "writeOnly": true, "maxLength": 200 }, - "telegram_group_chat_id": { + "email": { "type": "string", - "maxLength": 64 + "format": "email", + "maxLength": 254 }, - "telegram_bot_token": { + "phone": { "type": "string", - "writeOnly": true - }, - "telegram_bot_token_set": { - "type": "boolean", - "readOnly": true + "default": "", + "maxLength": 32 }, - "smtp_host": { + "password": { "type": "string", - "maxLength": 200 - }, - "smtp_port": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "writeOnly": true, + "maxLength": 128 }, - "smtp_username": { + "website": { "type": "string", - "maxLength": 200 + "default": "" + } + }, + "required": [ + "display_name", + "email", + "password" + ] + }, + "MemberTerm": { + "type": "object", + "properties": { + "plan_name": { + "type": "string" }, - "smtp_password": { + "starts_at": { "type": "string", - "writeOnly": true + "format": "date-time" }, - "smtp_password_set": { - "type": "boolean", - "readOnly": true + "ends_at": { + "type": "string", + "format": "date-time" }, - "smtp_use_tls": { + "current": { "type": "boolean" }, - "smtp_use_ssl": { + "blocks_borrowing": { "type": "boolean" - }, - "smtp_from_email": { - "oneOf": [ - { - "type": "string", - "format": "email", - "maxLength": 254 - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "slack_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 - }, - "slack_webhook_url_set": { + } + }, + "required": [ + "blocks_borrowing", + "current", + "ends_at", + "plan_name", + "starts_at" + ] + }, + "MemberUsage": { + "type": "object", + "properties": { + "usage_available": { "type": "boolean", - "readOnly": true + "description": "Active membership and membership module enabled." }, - "mattermost_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 + "loans": { + "$ref": "#/components/schemas/MemberLoanUsage" }, - "mattermost_webhook_url_set": { - "type": "boolean", - "readOnly": true + "machines": { + "$ref": "#/components/schemas/MemberMachineUsage" }, - "discord_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 + "bookings": { + "$ref": "#/components/schemas/MemberBookingUsage" }, - "discord_webhook_url_set": { - "type": "boolean", - "readOnly": true + "events": { + "$ref": "#/components/schemas/MemberEventUsage" }, - "default_loan_days": { + "payments": { + "$ref": "#/components/schemas/MemberPaymentUsage" + } + }, + "required": [ + "usage_available" + ] + }, + "MemberUsageSeries": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + } + }, + "chart_spec": { + "$ref": "#/components/schemas/ReportChartSpec" + } + }, + "required": [ + "chart_spec", + "rows" + ] + }, + "MemberVerificationAck": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "MemberVerificationRequest": { + "type": "object", + "properties": { + "membership_id": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "minimum": 1 }, - "presence_preset_minutes": {}, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "verified": { + "type": "boolean" + } + }, + "required": [ + "membership_id", + "verified" + ] + }, + "MemberVerificationResponse": { + "type": "object", + "properties": { + "membership_id": { + "type": "integer" }, - "updated_at": { + "display_name": { + "type": "string" + }, + "verified_at": { "type": "string", "format": "date-time", - "readOnly": true + "nullable": true } }, "required": [ - "archive_custody_state", - "branding_config", - "cors_allowed_origins", - "country", - "cover_image_key", - "cover_image_url", - "created_at", - "creation_invitation", - "discord_webhook_url_set", - "domain_verification_record", - "domain_verification_token", - "domain_verified_at", - "enabled_modules", - "frontend_domain_status", - "id", - "is_platform_subdomain", - "logo_key", - "logo_url", - "mattermost_webhook_url_set", - "name", - "opening_hours", - "platform_hosting", - "public_api_key", - "request_access", - "slack_webhook_url_set", - "slug", - "smtp_password_set", - "telegram_bot_token_set", - "timezone", - "unavailable_apps", - "updated_at" + "display_name", + "membership_id", + "verified_at" + ] + }, + "MemberWaiverResponse": { + "type": "object", + "properties": { + "has_waiver": { + "type": "boolean" + }, + "body": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "required": [ + "has_waiver" ] }, - "MakerspaceArchiveRequest": { + "MembershipAccount": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer", - "readOnly": true - }, - "requested_by": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "requested_by_username": { - "type": "string", - "readOnly": true, - "nullable": true + "status": { + "$ref": "#/components/schemas/MembershipAccountStatusEnum" }, - "requested_at": { + "renewal_cancelled_at": { "type": "string", "format": "date-time", - "readOnly": true + "nullable": true + } + }, + "required": [ + "renewal_cancelled_at", + "status" + ] + }, + "MembershipAccountStatusEnum": { + "enum": [ + "active", + "revoked" + ], + "type": "string", + "description": "* `active` - active\n* `revoked` - revoked" + }, + "MembershipApprovalOutcome": { + "oneOf": [ + { + "$ref": "#/components/schemas/AdminMembership" }, - "resolved_by": { + { + "$ref": "#/components/schemas/MembershipRequest" + } + ] + }, + "MembershipCreate": { + "type": "object", + "properties": { + "role_id": { "type": "integer", - "readOnly": true, - "nullable": true + "description": "Single-role set; omit role_ids." }, - "resolved_by_username": { - "type": "string", - "readOnly": true, - "nullable": true + "role_ids": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "Complete role set. Space Manager and Member must each be assigned alone." }, - "resolved_at": { + "username": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "maxLength": 150 }, - "reason": { + "email": { + "oneOf": [ + { + "type": "string", + "format": "email", + "maxLength": 254 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "first_name": { "type": "string", - "description": "Do not include personal data. Maximum 2,000 characters.", - "maxLength": 2000 + "maxLength": 150 }, - "resolution_note": { + "last_name": { "type": "string", - "readOnly": true + "maxLength": 150 }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/MakerspaceArchiveRequestStatusEnum" - } - ], - "readOnly": true, - "default": "pending" + "password": { + "type": "string", + "writeOnly": true } }, "required": [ - "id", - "makerspace", - "reason", - "requested_at", - "requested_by", - "requested_by_username", - "resolution_note", - "resolved_at", - "resolved_by", - "resolved_by_username", - "status" + "username" ] }, - "MakerspaceArchiveRequestStatusEnum": { + "MembershipDispositionEnum": { "enum": [ - "pending", - "approved", - "declined", - "withdrawn" + "import_membership", + "no_membership" ], "type": "string", - "description": "* `pending` - Pending\n* `approved` - Approved\n* `declined` - Declined\n* `withdrawn` - Withdrawn" + "description": "* `import_membership` - Import membership\n* `no_membership` - Do not import membership" }, - "MakerspaceCreationSetup": { + "MembershipGrowthReport": { "type": "object", "properties": { - "profile": { - "$ref": "#/components/schemas/CreationProfile" - }, - "modules": { + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/ModulesEnum" - }, - "maxItems": 60 + "type": "array", + "items": {} + } }, - "first_staff_invite": { - "$ref": "#/components/schemas/FirstStaffInvite" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MembershipGrowthRow" + } } - } + }, + "required": [ + "rows", + "typed_rows" + ] }, - "MakerspacePaymentSettings": { + "MembershipGrowthRow": { "type": "object", "properties": { - "default_currency": { - "type": "string", - "maxLength": 3 + "makerspace_id": { + "type": "integer" }, - "stripe_publishable_key": { + "period": { "type": "string", - "writeOnly": true, - "maxLength": 255 - }, - "stripe_publishable_key_set": { - "type": "boolean", - "readOnly": true + "format": "date" }, - "stripe_secret_key": { - "type": "string", - "writeOnly": true + "new_members": { + "type": "integer" }, - "stripe_secret_key_set": { - "type": "boolean", - "readOnly": true + "activated_members": { + "type": "integer" }, - "stripe_webhook_secret": { - "type": "string", - "writeOnly": true + "revoked_members": { + "type": "integer" }, - "stripe_webhook_secret_set": { - "type": "boolean", + "active_members": { + "type": "integer" + } + }, + "required": [ + "activated_members", + "active_members", + "new_members", + "period", + "revoked_members" + ] + }, + "MembershipList": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "connect_account_id": { - "type": "string", - "readOnly": true, - "nullable": true - }, - "connect_status": { + "user": { "allOf": [ { - "$ref": "#/components/schemas/ConnectStatusEnum" + "$ref": "#/components/schemas/User" } ], "readOnly": true }, - "connect_charges_enabled": { - "type": "boolean", - "readOnly": true - }, - "connect_payouts_enabled": { - "type": "boolean", - "readOnly": true - }, - "connect_status_updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "effective_mode": { - "type": "string", - "readOnly": true - }, - "loan_deposit_mode": { - "$ref": "#/components/schemas/LoanDepositModeEnum" - }, - "loan_deposit_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "loan_late_fee_per_day": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "loan_late_fee_cap": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "loan_grace_days": { + "makerspace_id": { "type": "integer", - "maximum": 32767, - "minimum": 0 - }, - "loan_deposit_blocks_issue": { - "type": "boolean" - } - }, - "required": [ - "connect_account_id", - "connect_charges_enabled", - "connect_payouts_enabled", - "connect_status", - "connect_status_updated_at", - "effective_mode", - "stripe_publishable_key_set", - "stripe_secret_key_set", - "stripe_webhook_secret_set" - ] - }, - "MakerspaceProfile": { - "type": "object", - "properties": { - "timezone": { - "type": "string", - "maxLength": 100 + "readOnly": true }, - "country": { + "makerspace_slug": { "type": "string", - "maxLength": 2 + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "opening_hours": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, + "role": { + "allOf": [ { - "type": "object", - "required": [ - "weekly", - "exceptions" - ], - "additionalProperties": false, - "properties": { - "weekly": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "additionalProperties": false, - "required": [ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun" - ], - "properties": { - "mon": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "tue": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "wed": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "thu": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "fri": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sat": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sun": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - } - } - } - ] - }, - "exceptions": { - "type": "object", - "maxProperties": 366, - "additionalProperties": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." - } - } + "$ref": "#/components/schemas/RoleCddEnum" } ], - "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." + "readOnly": true }, - "default_currency": { - "type": "string", - "maxLength": 3 + "assigned_role": { + "allOf": [ + { + "$ref": "#/components/schemas/MembershipRoleSummary" + } + ], + "readOnly": true, + "nullable": true }, - "timezone_choices": { + "role_ids": { "type": "array", "items": { - "type": "string" + "type": "integer" }, "readOnly": true }, - "country_choices": { + "roles": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/MembershipRole" }, "readOnly": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/Status37fEnum" + } + ], + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "payment": { + "allOf": [ + { + "$ref": "#/components/schemas/StaffPaymentSummary" + } + ], + "nullable": true, + "readOnly": true } }, "required": [ - "country", - "country_choices", - "default_currency", - "opening_hours", - "timezone", - "timezone_choices" + "assigned_role", + "created_at", + "id", + "makerspace_id", + "makerspace_slug", + "payment", + "role", + "role_ids", + "roles", + "status", + "user" ] }, - "MakerspaceUsage": { + "MembershipListResponse": { "type": "object", "properties": { - "makerspace_id": { + "count": { "type": "integer" }, - "managed": { - "type": "boolean" + "next": { + "type": "string", + "format": "uri", + "nullable": true }, - "resources": { + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/UsageResource" + "$ref": "#/components/schemas/AdminMembership" } } }, "required": [ - "makerspace_id", - "managed", - "resources" + "count", + "next", + "previous", + "results" ] }, - "ManagedPolicyMarker": { + "MembershipOutcome": { "type": "object", "properties": { - "feature": { - "type": "string" + "outcome": { + "$ref": "#/components/schemas/MembershipOutcomeOutcomeEnum" }, - "event": { - "type": "string" + "membership_id": { + "type": "integer" }, - "count": { - "type": "integer", - "minimum": 1 + "request_id": { + "type": "integer" + }, + "state": { + "type": "string" } }, "required": [ - "count", - "event", - "feature" + "outcome", + "state" ] }, - "ManualRefundError": { - "oneOf": [ - { - "$ref": "#/components/schemas/HardwareRequestError" - }, - { - "$ref": "#/components/schemas/ManualRefundValidationError" - } - ] + "MembershipOutcomeOutcomeEnum": { + "enum": [ + "joined", + "requested" + ], + "type": "string", + "description": "* `joined` - joined\n* `requested` - requested" }, - "ManualRefundReceipt": { + "MembershipPlan": { "type": "object", "properties": { - "method": { - "$ref": "#/components/schemas/Method2edEnum" + "id": { + "type": "integer", + "readOnly": true }, - "reference": { + "name": { "type": "string", - "default": "", - "maxLength": 64 + "maxLength": 120 }, - "refunded_at": { + "interval": { + "$ref": "#/components/schemas/IntervalEnum" + }, + "custom_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "amount": { "type": "string", - "format": "date-time" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "currency": { + "type": "string", + "maxLength": 3 + }, + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "loan_max_active": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "loan_max_items": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "auto_renew": { + "type": "boolean" + }, + "renewal_open_days": { + "type": "integer", + "maximum": 365, + "minimum": 0 + }, + "renewal_grace_days": { + "type": "integer", + "maximum": 365, + "minimum": 0 + }, + "renewal_retries": { + "type": "integer", + "maximum": 3, + "minimum": 0 + }, + "reminder_days_before": {}, + "reminder_days_after": {} + }, + "required": [ + "created_at", + "id", + "interval", + "name", + "updated_at" + ] + }, + "MembershipPolicyEnum": { + "enum": [ + "request", + "open", + "invite_only" + ], + "type": "string", + "description": "* `request` - Request\n* `open` - Open\n* `invite_only` - Invite only" + }, + "MembershipReason": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "maxLength": 1000 } }, "required": [ - "method", - "refunded_at" + "reason" ] }, - "ManualRefundValidationError": { + "MembershipRequest": { "type": "object", "properties": { - "amount": { - "type": "array", - "items": { - "type": "string" - } + "id": { + "type": "integer", + "readOnly": true }, - "reason": { - "type": "array", - "items": { - "type": "string" - } + "kind": { + "$ref": "#/components/schemas/MembershipRequestKindEnum" }, - "receipt": {}, - "non_field_errors": { + "state": { + "$ref": "#/components/schemas/State9efEnum" + }, + "invite_email": { + "type": "string", + "maxLength": 254 + }, + "user": { + "allOf": [ + { + "$ref": "#/components/schemas/MembershipRequestUser" + } + ], + "nullable": true, + "readOnly": true + }, + "assigned_role": { + "allOf": [ + { + "$ref": "#/components/schemas/MembershipRequestRole" + } + ], + "nullable": true, + "readOnly": true + }, + "decision_note": { + "type": "string" + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "decided_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "application_answers": { + "nullable": true, + "readOnly": true + }, + "vouches": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/Vouch" } + }, + "admission_payment": { + "allOf": [ + { + "$ref": "#/components/schemas/AdmissionPayment" + } + ], + "nullable": true, + "readOnly": true + }, + "expires_at": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "required": [ + "admission_payment", + "application_answers", + "assigned_role", + "created_at", + "id", + "kind", + "state", + "user", + "vouches" + ] + }, + "MembershipRequestCreate": { + "type": "object", + "properties": { + "application_answers": { + "nullable": true + }, + "website": { + "type": "string", + "writeOnly": true } } }, - "ManualSettlement": { + "MembershipRequestKindEnum": { + "enum": [ + "request", + "invite" + ], + "type": "string", + "description": "* `request` - Request\n* `invite` - Invite" + }, + "MembershipRequestListResponse": { "type": "object", - "description": "The receipt required to mark a charge paid offline.\n\nMethod and received date are REQUIRED: the point of the ledger is that a settled\ncharge can always say how and when the money arrived, and an optional field would\nquietly reproduce the bare boolean it replaces. `reference` stays optional because\ncash genuinely has none.", "properties": { - "method": { - "$ref": "#/components/schemas/Method2edEnum" + "count": { + "type": "integer" }, - "reference": { + "next": { "type": "string", - "default": "", - "maxLength": 64 + "format": "uri", + "nullable": true }, - "received_at": { + "previous": { "type": "string", - "format": "date-time" + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MembershipRequest" + } } }, "required": [ - "method", - "received_at" + "count", + "next", + "previous", + "results" ] }, - "Material": { + "MembershipRequestRole": { "type": "object", "properties": { "id": { - "type": "integer", - "readOnly": true - }, - "machine_type": { "type": "integer" }, "name": { - "type": "string", - "maxLength": 100 - }, - "colours": {}, - "is_active": { - "type": "boolean" + "type": "string" } }, "required": [ - "colours", "id", - "machine_type", "name" ] }, - "Measurement883Enum": { - "enum": [ - "count", - "grams" - ], - "type": "string", - "description": "* `count` - Count\n* `grams` - Grams" - }, - "MemberAccountability": { + "MembershipRequestUser": { "type": "object", "properties": { - "membership_active": { - "type": "boolean" + "id": { + "type": "integer" }, - "waiver_acceptance_required": { - "type": "boolean" + "username": { + "type": "string" }, - "restriction_code": { + "email": { "type": "string", - "nullable": true + "format": "email" } }, "required": [ - "membership_active", - "restriction_code", - "waiver_acceptance_required" + "email", + "id", + "username" ] }, - "MemberActivity": { + "MembershipRevenueReport": { "type": "object", "properties": { - "active_hardware_loans": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberLoanActivity" - } - }, - "print_requests": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberPrintActivity" - } - }, - "machine_service_requests": { + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/MemberMachineServiceActivity" - } - }, - "bookings": { - "type": "object", - "additionalProperties": { "type": "array", "items": {} } }, - "event_registrations": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberEventRegistrationActivity" - } - }, - "recent_presence_sessions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberPresenceActivity" - } - }, - "currently_checked_in": { - "type": "boolean" - }, - "accountability": { - "$ref": "#/components/schemas/MemberAccountability" - }, - "loan_history": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberLoanHistory" - } - }, - "request_history": { + "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/MemberRequestHistory" + "$ref": "#/components/schemas/MembershipRevenueRow" } }, - "membership_dues": { - "$ref": "#/components/schemas/MemberDues" - }, - "membership_term": { - "allOf": [ - { - "$ref": "#/components/schemas/MemberTerm" - } - ], - "nullable": true + "meta": { + "type": "object", + "additionalProperties": {} }, - "notices": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberNotice" - } + "report_key": { + "type": "string" } }, "required": [ - "accountability", - "active_hardware_loans", - "currently_checked_in", - "recent_presence_sessions" + "meta", + "report_key", + "rows", + "typed_rows" ] }, - "MemberActivityReport": { + "MembershipRevenueRow": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "makerspace_id": { + "type": "integer" + }, + "period": { + "type": "string", + "format": "date" + }, + "currency": { + "type": "string" + }, + "plan_id": { + "type": "integer" + }, + "plan_name": { + "type": "string" + }, + "recurring_members": { + "type": "integer" + }, + "mrr": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + }, + "arr": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + }, + "arpu": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberActivityRow" - } + "churned_members": { + "type": "integer" + }, + "reactivated_members": { + "type": "integer" } }, "required": [ - "rows", - "typed_rows" + "arpu", + "arr", + "churned_members", + "currency", + "mrr", + "period", + "plan_id", + "plan_name", + "reactivated_members", + "recurring_members" ] }, - "MemberActivityRow": { + "MembershipRole": { "type": "object", "properties": { - "makerspace_id": { + "id": { "type": "integer" }, - "makerspace_name": { + "slug": { "type": "string" }, - "membership_policy": { + "name": { "type": "string" - }, - "referrals_enabled": { - "type": "boolean" - }, - "new_members": { - "type": "integer", - "description": "Current activated_at values in the selected [start, end) range, not event history." - }, - "active_members": { + } + }, + "required": [ + "id", + "name", + "slug" + ] + }, + "MembershipRoleSummary": { + "type": "object", + "properties": { + "id": { "type": "integer", - "description": "Current membership snapshot." + "readOnly": true }, - "revoked_members": { - "type": "integer", - "description": "Current revoked_at values in the selected [start, end) range, not event history." + "name": { + "type": "string", + "readOnly": true }, - "pending_requests": { - "type": "integer", - "description": "Current membership-request snapshot." + "slug": { + "type": "string", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "open_invites": { - "type": "integer", - "description": "Current membership-request snapshot." + "legacy_role": { + "readOnly": true, + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/LegacyRoleEnum" + }, + { + "$ref": "#/components/schemas/NullEnum" + } + ] }, - "referred_joins": { - "type": "integer", - "description": "Active automatic referrals with decided_at in the selected [start, end) range." + "is_default": { + "type": "boolean", + "readOnly": true }, - "verified_members": { - "type": "integer", - "description": "Current verified_at snapshot." + "is_protected": { + "type": "boolean", + "readOnly": true } }, "required": [ - "active_members", - "makerspace_name", - "membership_policy", - "new_members", - "open_invites", - "pending_requests", - "referrals_enabled", - "referred_joins", - "revoked_members", - "verified_members" + "id", + "is_default", + "is_protected", + "legacy_role", + "name", + "slug" ] }, - "MemberBooking": { + "MembershipTerm": { "type": "object", "properties": { - "public_token": { - "type": "string", - "format": "uuid", + "id": { + "type": "integer", "readOnly": true }, - "space_name": { - "type": "string", + "membership": { + "type": "integer", "readOnly": true }, - "timezone": { + "plan": { + "type": "integer", + "readOnly": true + }, + "plan_name": { "type": "string", "readOnly": true }, @@ -84117,925 +106343,993 @@ "status": { "allOf": [ { - "$ref": "#/components/schemas/Status2caEnum" + "$ref": "#/components/schemas/MembershipTermStatusEnum" } ], "readOnly": true }, - "cancellation_cutoff_at": { + "renewal_payment": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { "type": "string", "format": "date-time", "readOnly": true - }, - "can_cancel": { - "type": "boolean", - "readOnly": true } }, "required": [ - "can_cancel", - "cancellation_cutoff_at", + "created_at", + "created_by", "ends_at", - "public_token", - "space_name", + "id", + "membership", + "plan", + "plan_name", + "renewal_payment", "starts_at", - "status", - "timezone" + "status" ] }, - "MemberBookingList": { + "MembershipTermCreate": { "type": "object", "properties": { - "count": { + "plan_id": { "type": "integer" }, - "next": { - "type": "string", - "format": "uri", - "nullable": true - }, - "previous": { + "starts_at": { "type": "string", - "format": "uri", + "format": "date-time", "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberBooking" - } } }, "required": [ - "count", - "next", - "previous", - "results" + "plan_id" ] }, - "MemberCalendarFeedIssue": { - "type": "object", - "properties": { - "confirm_bearer_risk": { - "type": "boolean" - } - }, - "required": [ - "confirm_bearer_risk" - ] + "MembershipTermStatusEnum": { + "enum": [ + "active", + "expired", + "cancelled" + ], + "type": "string", + "description": "* `active` - Active\n* `expired` - Expired\n* `cancelled` - Cancelled" }, - "MemberCalendarFeedIssued": { + "MessagingCatalogEntry": { "type": "object", "properties": { - "feed_url": { - "type": "string", - "format": "uri", - "readOnly": true + "feature": { + "type": "string" }, - "token_hint": { - "type": "string", - "readOnly": true + "event": { + "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "audience": { + "$ref": "#/components/schemas/Audience817Enum" + }, + "allowed_projection_keys": { + "type": "array", + "items": { + "type": "string" + } + }, + "supported_languages": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "created_at", - "feed_url", - "token_hint" + "allowed_projection_keys", + "audience", + "event", + "feature", + "supported_languages" ] }, - "MemberCalendarFeedState": { + "MessagingChannel": { "type": "object", "properties": { - "enabled": { - "type": "boolean", - "readOnly": true + "available": { + "type": "boolean" }, - "token_hint": { - "type": "string", - "readOnly": true, - "nullable": true + "consented": { + "type": "boolean" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "phone_verified": { + "type": "boolean" }, - "rotated_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "phone_masked": { + "type": "string" + }, + "suppressed": { + "type": "boolean" } }, "required": [ - "created_at", - "enabled", - "rotated_at", - "token_hint" + "available", + "consented", + "phone_masked", + "phone_verified", + "suppressed" ] }, - "MemberCard": { + "MessagingSettings": { "type": "object", - "description": "Never carries the photo key or a URL that outlives the response.", "properties": { - "id": { - "type": "integer", - "readOnly": true + "use_platform_sender": { + "type": "boolean" }, - "card_number": { - "type": "integer", - "readOnly": true + "sms_account_sid": { + "oneOf": [ + { + "type": "string", + "pattern": "^(AC[0-9a-fA-F]{32})?$" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "printed_name": { + "sms_from_number": { "type": "string", - "readOnly": true + "maxLength": 32 }, - "membership_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "sms_auth_token": { + "type": "string", + "writeOnly": true, + "maxLength": 4096 }, - "is_active": { - "type": "boolean", - "readOnly": true + "whatsapp_phone_number_id": { + "oneOf": [ + { + "type": "string", + "pattern": "^\\d{1,64}$" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "photo_set": { - "type": "boolean", - "readOnly": true + "whatsapp_business_account_id": { + "oneOf": [ + { + "type": "string", + "pattern": "^\\d{1,64}$" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "photo_consent_at": { + "whatsapp_access_token": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "writeOnly": true, + "maxLength": 8192 }, - "qr_active": { + "whatsapp_app_secret": { + "type": "string", + "writeOnly": true, + "maxLength": 4096 + }, + "sms_auth_token_set": { "type": "boolean", "readOnly": true }, - "print_count": { - "type": "integer", + "whatsapp_access_token_set": { + "type": "boolean", "readOnly": true }, - "last_printed_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "issued_at": { - "type": "string", - "format": "date-time", + "whatsapp_app_secret_set": { + "type": "boolean", "readOnly": true }, - "revoked_at": { + "whatsapp_webhook_verify_token": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "readOnly": true }, - "revoked_reason": { - "type": "string", + "effective_source": { + "type": "object", + "additionalProperties": { + "enum": [ + "own", + "platform", + "none" + ], + "type": "string", + "description": "* `own` - own\n* `platform` - platform\n* `none` - none" + }, "readOnly": true }, - "created_at": { - "type": "string", - "format": "date-time", + "platform_available": { + "type": "object", + "additionalProperties": { + "type": "boolean" + }, "readOnly": true }, - "updated_at": { - "type": "string", - "format": "date-time", + "webhook_urls": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "readOnly": true } }, "required": [ - "card_number", - "created_at", - "id", - "is_active", - "issued_at", - "last_printed_at", - "membership_id", - "photo_consent_at", - "photo_set", - "print_count", - "printed_name", - "qr_active", - "revoked_at", - "revoked_reason", - "updated_at" + "effective_source", + "platform_available", + "sms_auth_token_set", + "webhook_urls", + "whatsapp_access_token_set", + "whatsapp_app_secret_set", + "whatsapp_webhook_verify_token" ] }, - "MemberCardIssue": { + "MessagingTest": { "type": "object", "properties": { - "printed_name": { + "channel": { + "$ref": "#/components/schemas/Channel06eEnum" + }, + "feature": { "type": "string", - "maxLength": 200 + "default": "hardware_requests" + }, + "event": { + "type": "string", + "default": "accepted" } - } + }, + "required": [ + "channel" + ] }, - "MemberCardPhotoFinalize": { + "MessagingTestResponse": { "type": "object", "properties": { - "object_key": { - "type": "string", - "maxLength": 300 + "delivery_id": { + "type": "integer" }, - "content_type": { - "type": "string", - "maxLength": 64 + "status": { + "type": "string" }, - "consent": { - "type": "boolean" + "skip_reason": { + "type": "string" } }, "required": [ - "consent", - "content_type", - "object_key" + "delivery_id", + "skip_reason", + "status" ] }, - "MemberCardPhotoPresign": { + "Method2edEnum": { + "enum": [ + "cash", + "upi", + "bank_transfer", + "card_machine", + "cheque", + "other" + ], + "type": "string", + "description": "* `cash` - Cash\n* `upi` - UPI\n* `bank_transfer` - Bank transfer\n* `card_machine` - Card machine\n* `cheque` - Cheque\n* `other` - Other" + }, + "MigrationExportCreate": { "type": "object", "properties": { - "content_type": { + "approval_id": { "type": "string", - "maxLength": 64 + "format": "uuid" + }, + "target_age_recipient": { + "type": "string", + "writeOnly": true, + "maxLength": 256 } }, "required": [ - "content_type" + "approval_id", + "target_age_recipient" ] }, - "MemberCardPrint": { + "MigrationExportJob": { "type": "object", "properties": { - "card_ids": { - "type": "array", - "items": { - "type": "integer" - } + "id": { + "type": "string", + "format": "uuid", + "readOnly": true + }, + "status": { + "$ref": "#/components/schemas/StatusE1dEnum" + }, + "failure_code": { + "oneOf": [ + { + "$ref": "#/components/schemas/FailureCodeEnum" + }, + { + "$ref": "#/components/schemas/BlankEnum" + } + ] + }, + "failure_detail": { + "type": "string", + "maxLength": 500 + }, + "manifest": {}, + "closure_digest": { + "type": "string" + }, + "archive_digest": { + "type": "string" + }, + "format_version": { + "type": "integer" + }, + "source_retention_notice": { + "type": "string", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "started_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "completed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "archive_digest", + "closure_digest", + "created_at", + "expires_at", + "format_version", + "id", + "source_retention_notice" + ] + }, + "MobilePaymentIntentResponse": { + "oneOf": [ + { + "$ref": "#/components/schemas/StripeMobilePaymentIntentResponse" }, - "preset": { - "allOf": [ - { - "$ref": "#/components/schemas/PresetEnum" - } - ], - "default": "sheet" + { + "$ref": "#/components/schemas/RazorpayMobilePaymentIntentResponse" + } + ], + "discriminator": { + "propertyName": "vendor", + "mapping": { + "stripe": "#/components/schemas/StripeMobilePaymentIntentResponse", + "razorpay": "#/components/schemas/RazorpayMobilePaymentIntentResponse" } } }, - "MemberCardReissue": { + "MobileReadiness": { "type": "object", "properties": { - "reason": { - "$ref": "#/components/schemas/MemberCardReissueReasonEnum" + "platforms": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/PlatformReadiness" + } } }, "required": [ - "reason" + "platforms" ] }, - "MemberCardReissueReasonEnum": { + "Mode087Enum": { "enum": [ - "lost", - "stolen", - "damaged", - "renewed" + "preview", + "apply" ], "type": "string", - "description": "* `lost` - lost\n* `stolen` - stolen\n* `damaged` - damaged\n* `renewed` - renewed" - }, - "MemberCardResolve": { - "type": "object", - "properties": { - "payload": { - "type": "string", - "maxLength": 64 - } - }, - "required": [ - "payload" - ] + "description": "* `preview` - Preview\n* `apply` - Apply" }, - "MemberCardResolveResult": { + "ModuleAction": { "type": "object", + "description": "The one field either mutation takes.\n\nA real class rather than `inline_serializer`, which returns an *instance* and so\ncannot be called with `data=` at request time.", "properties": { - "outcome": { - "$ref": "#/components/schemas/MemberCardResolveResultOutcomeEnum" - }, - "card_id": { - "type": "integer" - }, - "card_number": { - "type": "integer" - }, - "printed_name": { - "type": "string" - }, - "membership_id": { - "type": "integer" - }, - "membership_status": { + "key": { "type": "string" - }, - "photo_url": { - "type": "string", - "nullable": true } }, "required": [ - "outcome" + "key" ] }, - "MemberCardResolveResultOutcomeEnum": { + "ModulesEnum": { "enum": [ - "ok", - "revoked", - "inactive" + "asset_units", + "backup", + "bookings", + "bulk_import", + "containers", + "devices", + "discord", + "email", + "events", + "evidence_uploads", + "guest_handover", + "machine_service", + "machines", + "maintenance", + "mattermost", + "member_accounts", + "membership", + "mobile", + "notifications", + "payments", + "printing", + "procurement", + "public_inventory", + "qr_management", + "qr_print_batches", + "reports", + "request_workflow", + "scanner", + "slack", + "sms", + "staff_admin", + "stock_transfers", + "stocktake", + "telegram", + "webhook", + "whatsapp" ], "type": "string", - "description": "* `ok` - ok\n* `revoked` - revoked\n* `inactive` - inactive" - }, - "MemberCardRevoke": { - "type": "object", - "properties": { - "reason": { - "type": "string", - "maxLength": 32 - } - } + "description": "* `asset_units` - asset_units\n* `backup` - backup\n* `bookings` - bookings\n* `bulk_import` - bulk_import\n* `containers` - containers\n* `devices` - devices\n* `discord` - discord\n* `email` - email\n* `events` - events\n* `evidence_uploads` - evidence_uploads\n* `guest_handover` - guest_handover\n* `machine_service` - machine_service\n* `machines` - machines\n* `maintenance` - maintenance\n* `mattermost` - mattermost\n* `member_accounts` - member_accounts\n* `membership` - membership\n* `mobile` - mobile\n* `notifications` - notifications\n* `payments` - payments\n* `printing` - printing\n* `procurement` - procurement\n* `public_inventory` - public_inventory\n* `qr_management` - qr_management\n* `qr_print_batches` - qr_print_batches\n* `reports` - reports\n* `request_workflow` - request_workflow\n* `scanner` - scanner\n* `slack` - slack\n* `sms` - sms\n* `staff_admin` - staff_admin\n* `stock_transfers` - stock_transfers\n* `stocktake` - stocktake\n* `telegram` - telegram\n* `webhook` - webhook\n* `whatsapp` - whatsapp" }, - "MemberCardTemplate": { + "MostLentReport": { "type": "object", "properties": { - "version": { - "type": "integer" - }, - "page": { - "type": "string" - }, - "orientation": { - "type": "string" - }, - "card_width_mm": { - "type": "number", - "format": "double" - }, - "card_height_mm": { - "type": "number", - "format": "double" - }, - "margin_mm": { - "type": "number", - "format": "double" - }, - "gap_mm": { - "type": "number", - "format": "double" - }, - "front_fields": { + "rows": { "type": "array", "items": { - "type": "string" + "type": "array", + "items": {} } }, - "back_text": { - "type": "string" - }, - "include_photo": { - "type": "boolean" - }, - "include_qr": { - "type": "boolean" - }, - "name_font_size_pt": { - "type": "integer" - }, - "font_size_pt": { - "type": "integer" - }, - "crop_marks": { - "type": "boolean" - } - } - }, - "MemberCertification": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "name": { - "type": "string" - }, - "machine_type_name": { - "type": "string" - }, - "type_active": { - "type": "boolean" - }, - "granted_at": { - "type": "string", - "format": "date-time" - }, - "expires_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "revoked_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "is_live": { - "type": "boolean", - "readOnly": true + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MostLentReportRow" + } } }, "required": [ - "id", - "is_live", - "machine_type_name", - "name", - "type_active" + "rows", + "typed_rows" ] }, - "MemberCertificationPage": { + "MostLentReportRow": { "type": "object", "properties": { - "count": { + "makerspace_id": { "type": "integer" }, - "next": { + "period": { "type": "string", - "nullable": true + "format": "date", + "nullable": true, + "description": "Null for legacy loans without an issue timestamp." }, - "previous": { - "type": "string", - "nullable": true + "product_id": { + "type": "integer", + "nullable": true, + "description": "Null identifies the Other series." }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberCertification" - } + "product_name": { + "type": "string" + }, + "times_lent": { + "type": "integer" + }, + "total_quantity_lent": { + "type": "integer" } }, "required": [ - "count", - "next", - "previous", - "results" + "period", + "product_id", + "product_name", + "times_lent", + "total_quantity_lent" ] }, - "MemberClaimCode": { + "MoveToInventoryRequest": { "type": "object", "properties": { - "id": { + "mode": { + "$ref": "#/components/schemas/MoveToInventoryRequestModeEnum" + }, + "product_id": { "type": "integer", - "readOnly": true + "nullable": true }, - "membership_id": { + "quantity": { "type": "integer", - "readOnly": true + "minimum": 1 }, - "member_display_name": { - "type": "string", - "readOnly": true + "box": { + "type": "integer", + "nullable": true }, - "issued_by_id": { + "category": { "type": "integer", - "readOnly": true, "nullable": true }, - "issued_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "tracking_mode": { + "allOf": [ + { + "$ref": "#/components/schemas/MoveToInventoryRequestTrackingModeEnum" + } + ], + "default": "quantity" }, - "expires_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "is_public": { + "type": "boolean", + "default": true }, - "consumed_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "public_availability_mode": { + "allOf": [ + { + "$ref": "#/components/schemas/MoveToInventoryRequestPublicAvailabilityModeEnum" + } + ], + "default": "status_only" }, - "revoked_at": { + "show_public_count": { + "type": "boolean", + "default": false + }, + "public_self_checkout_enabled": { + "type": "boolean", + "default": false + }, + "name": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "maxLength": 200 }, - "status": { + "description": { "type": "string", - "readOnly": true + "default": "" } }, "required": [ - "consumed_at", - "expires_at", - "id", - "issued_at", - "issued_by_id", - "member_display_name", - "membership_id", - "revoked_at", - "status" + "mode", + "quantity" ] }, - "MemberClaimCodeIssueRequest": { - "type": "object", - "properties": { - "membership_id": { - "type": "integer", - "minimum": 1 - } - }, - "required": [ - "membership_id" - ] + "MoveToInventoryRequestModeEnum": { + "enum": [ + "create", + "topup" + ], + "type": "string", + "description": "* `create` - create\n* `topup` - topup" }, - "MemberClaimCodeIssueResponse": { + "MoveToInventoryRequestPublicAvailabilityModeEnum": { + "enum": [ + "exact_count", + "status_only", + "hidden" + ], + "type": "string", + "description": "* `exact_count` - exact_count\n* `status_only` - status_only\n* `hidden` - hidden" + }, + "MoveToInventoryRequestTrackingModeEnum": { + "enum": [ + "quantity", + "individual" + ], + "type": "string", + "description": "* `quantity` - quantity\n* `individual` - individual" + }, + "MoveToPrintingRequest": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "target": { + "$ref": "#/components/schemas/TargetEnum" }, - "membership_id": { + "printer": { "type": "integer", - "readOnly": true + "nullable": true }, - "member_display_name": { + "material": { "type": "string", - "readOnly": true + "maxLength": 100 }, - "issued_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "color": { + "type": "string", + "maxLength": 100 }, - "issued_at": { + "brand": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 100 }, - "expires_at": { + "lot_code": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 100 }, - "consumed_at": { + "initial_weight_grams": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,6}(?:\\.\\d{0,2})?$" }, - "revoked_at": { + "remaining_weight_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,6}(?:\\.\\d{0,2})?$" + }, + "is_active": { + "type": "boolean" + }, + "opened_at": { "type": "string", "format": "date-time", - "readOnly": true, "nullable": true }, - "status": { + "name": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "code": { + "model": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "qr_svg": { - "type": "string", - "readOnly": true + "status": { + "$ref": "#/components/schemas/MoveToPrintingRequestStatusEnum" + }, + "notes": { + "type": "string" } }, "required": [ - "code", - "consumed_at", - "expires_at", - "id", - "issued_at", - "issued_by_id", - "member_display_name", - "membership_id", - "qr_svg", - "revoked_at", - "status" + "target" ] }, - "MemberDues": { + "MoveToPrintingRequestStatusEnum": { + "enum": [ + "active", + "maintenance", + "offline" + ], + "type": "string", + "description": "* `active` - active\n* `maintenance` - maintenance\n* `offline` - offline" + }, + "MyMembershipRequest": { "type": "object", "properties": { - "dues_amount": { + "id": { + "type": "integer" + }, + "makerspace": { + "type": "object", + "additionalProperties": {} + }, + "state": { "type": "string" }, - "outstanding_by_currency": { + "kind": { + "type": "string" + }, + "admission_payment": { "type": "object", - "additionalProperties": { - "type": "string" - }, + "additionalProperties": {}, + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time", "nullable": true } }, "required": [ - "dues_amount", - "outstanding_by_currency" + "admission_payment", + "expires_at", + "id", + "kind", + "makerspace", + "state" ] }, - "MemberEventRegistrationActivity": { + "MyMembershipRow": { "type": "object", "properties": { - "registration_id": { - "type": "integer" - }, - "checkin_token": { - "type": "string", - "format": "uuid", - "nullable": true - }, - "event_title": { - "type": "string" - }, - "starts_at": { - "type": "string", - "format": "date-time" + "makerspace": { + "type": "object", + "additionalProperties": {} }, - "ends_at": { - "type": "string", - "format": "date-time" + "membership_status": { + "type": "string" }, - "status": { + "role": { "type": "string" }, - "cancellation_cutoff_at": { - "type": "string", - "format": "date-time" + "actions": { + "type": "array", + "items": { + "type": "string" + } }, - "can_cancel": { + "can_refer": { "type": "boolean" }, - "waitlist_position": { - "type": "integer", - "nullable": true - }, - "feedback_available": { + "can_verify": { "type": "boolean" }, - "feedback_path": { + "verified_at": { "type": "string", + "format": "date-time", "nullable": true }, - "certificate": { - "type": "object", - "additionalProperties": {}, - "nullable": true + "referrals_enabled": { + "type": "boolean" + }, + "waiver_accepted": { + "type": "boolean" + }, + "waiver_acceptance_required": { + "type": "boolean" } }, "required": [ - "can_cancel", - "cancellation_cutoff_at", - "certificate", - "checkin_token", - "ends_at", - "event_title", - "feedback_available", - "feedback_path", - "registration_id", - "starts_at", - "status", - "waitlist_position" + "actions", + "can_refer", + "can_verify", + "makerspace", + "membership_status", + "referrals_enabled", + "role", + "verified_at", + "waiver_acceptance_required", + "waiver_accepted" ] }, - "MemberKitPreviewInput": { + "MyMemberships": { "type": "object", "properties": { - "payload": { - "type": "string", - "maxLength": 64 + "memberships": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MyMembershipRow" + } }, - "phase": { - "$ref": "#/components/schemas/PhaseEnum" + "requests": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MyMembershipRequest" + } } }, "required": [ - "payload", - "phase" + "memberships", + "requests" ] }, - "MemberLoanActivity": { + "NativeApple": { "type": "object", "properties": { - "request_id": { - "type": "integer" - }, - "renewal_count": { - "type": "integer" - }, - "can_renew": { + "enabled": { "type": "boolean" + } + }, + "required": [ + "enabled" + ] + }, + "NativeConfig": { + "type": "object", + "properties": { + "min_app_version": { + "$ref": "#/components/schemas/NativeMinVersion" }, - "renewal_refusal": { - "type": "string", - "nullable": true - }, - "label": { - "type": "string" - }, - "checked_out_at": { - "type": "string", - "format": "date-time" - }, - "due_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "overdue": { - "type": "boolean" + "providers": { + "$ref": "#/components/schemas/NativeProviders" } }, "required": [ - "can_renew", - "checked_out_at", - "due_at", - "label", - "overdue", - "renewal_count", - "renewal_refusal", - "request_id" + "min_app_version", + "providers" ] }, - "MemberLoanHistory": { + "NativeGoogle": { "type": "object", "properties": { - "label": { + "android_client_id": { "type": "string" }, - "checked_out_at": { - "type": "string", - "format": "date-time" - }, - "returned_at": { + "ios_client_id": { + "type": "string" + } + }, + "required": [ + "android_client_id", + "ios_client_id" + ] + }, + "NativeMinVersion": { + "type": "object", + "properties": { + "android": { "type": "string", - "format": "date-time", - "nullable": true + "nullable": true, + "description": "Highest dotted-numeric minimum among approved global production Android registrations, or null." }, - "due_at": { + "ios": { "type": "string", - "format": "date-time", - "nullable": true - }, - "returned_late": { - "type": "boolean" + "nullable": true, + "description": "Highest dotted-numeric minimum among approved global production Apple registrations, or null." } }, "required": [ - "checked_out_at", - "due_at", - "label", - "returned_at", - "returned_late" + "android", + "ios" ] }, - "MemberMachineServiceActivity": { + "NativeOidc": { "type": "object", "properties": { - "machine_type": { + "key": { "type": "string" }, - "title": { + "display_name": { "type": "string" }, - "status": { + "client_id": { "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "queue_position": { - "type": "integer", - "nullable": true + "issuer": { + "type": "string" } }, "required": [ - "created_at", - "queue_position", - "status", - "title" + "client_id", + "display_name", + "issuer", + "key" ] }, - "MemberNotice": { + "NativeProviders": { "type": "object", - "description": "A fact about the reader, derived from their own rows.\n\nNot `notifications.Notification`: that table is makerspace-wide, has no recipient\nand shares one `read_at`, so serving it would leak staff alerts.", "properties": { - "level": { - "type": "string" + "password": { + "type": "boolean" }, - "event": { - "type": "string" + "phone": { + "type": "boolean" }, - "title": { - "type": "string" + "google": { + "allOf": [ + { + "$ref": "#/components/schemas/NativeGoogle" + } + ], + "nullable": true }, - "body": { - "type": "string" + "apple": { + "allOf": [ + { + "$ref": "#/components/schemas/NativeApple" + } + ], + "nullable": true + }, + "oidc": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NativeOidc" + } } }, "required": [ - "body", - "event", - "level", - "title" + "apple", + "google", + "oidc", + "password", + "phone" ] }, - "MemberPayment": { + "NeedsFixAction": { + "type": "object", + "properties": { + "action": { + "$ref": "#/components/schemas/NeedsFixActionActionEnum" + }, + "quantity": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "action", + "quantity" + ] + }, + "NeedsFixActionActionEnum": { + "enum": [ + "repair", + "scrap", + "shelve" + ], + "type": "string", + "description": "* `repair` - repair\n* `scrap` - scrap\n* `shelve` - shelve" + }, + "NegativesEnum": { + "enum": [ + "allow", + "clip" + ], + "type": "string", + "description": "* `allow` - allow\n* `clip` - clip" + }, + "Notification": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "subject_type": { - "$ref": "#/components/schemas/SubjectTypeEnum" - }, - "subject_label": { - "type": "string", + "level": { + "allOf": [ + { + "$ref": "#/components/schemas/LevelEnum" + } + ], "readOnly": true }, - "status": { - "$ref": "#/components/schemas/Status66aEnum" - }, - "amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "currency": { - "type": "string", - "pattern": "^[a-z]{3}$", - "maxLength": 3 - }, - "due_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "due_date": { + "event": { "type": "string", - "nullable": true, "readOnly": true }, - "due_state": { + "title": { "type": "string", "readOnly": true }, - "checkout_url": { + "body": { "type": "string", "readOnly": true }, - "online_payment_available": { - "type": "boolean", - "description": "Whether offering to pay online is honest for this charge.\n\nResolved once per makerspace by the view and passed in: doing it per row would\nre-read settings and credentials for every charge in the list. Falls back to a\nlive check only when a caller has not supplied it.", - "readOnly": true - }, - "settlement": { - "allOf": [ - { - "$ref": "#/components/schemas/MemberSettlement" - } - ], - "nullable": true, - "readOnly": true - }, - "refunded_amount": { - "type": "number", - "format": "double", + "url_path": { + "type": "string", "readOnly": true }, - "refunds": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberRefund" - }, - "readOnly": true + "read_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true }, "created_at": { "type": "string", @@ -85044,1101 +107338,1041 @@ } }, "required": [ - "amount", - "checkout_url", + "body", "created_at", - "currency", - "due_date", - "due_state", + "event", "id", - "online_payment_available", - "refunded_amount", - "refunds", - "settlement", - "subject_label", - "subject_type" + "level", + "read_at", + "title", + "url_path" ] }, - "MemberPresenceActivity": { + "NotificationChannel": { "type": "object", "properties": { - "started_at": { - "type": "string", - "format": "date-time" - }, - "expires_at": { - "type": "string", - "format": "date-time" - }, - "ended_at": { - "type": "string", - "format": "date-time", - "nullable": true + "key": { + "$ref": "#/components/schemas/Key292Enum" }, - "end_reason": { + "label": { "type": "string" - }, - "active": { - "type": "boolean" } }, "required": [ - "active", - "end_reason", - "ended_at", - "expires_at", - "started_at" + "key", + "label" ] }, - "MemberPrintActivity": { + "NotificationDestination": { "type": "object", "properties": { - "public_token": { + "id": { + "type": "integer", + "readOnly": true + }, + "channel": { + "allOf": [ + { + "$ref": "#/components/schemas/Channel59dEnum" + } + ], + "readOnly": true + }, + "label": { "type": "string", - "format": "uuid" + "readOnly": true }, - "status": { - "type": "string" + "telegram_chat_id": { + "type": "string", + "readOnly": true }, - "title": { - "type": "string" + "is_active": { + "type": "boolean", + "readOnly": true }, - "created_at": { + "payload_version": { + "allOf": [ + { + "$ref": "#/components/schemas/NotificationDestinationPayloadVersionEnum" + } + ], + "readOnly": true + }, + "credential_set": { "type": "string", - "format": "date-time" + "readOnly": true }, - "accepted_at": { + "signing_secret_set": { "type": "string", - "format": "date-time", - "nullable": true + "readOnly": true }, - "started_at": { + "scope": { "type": "string", - "format": "date-time", - "nullable": true + "readOnly": true }, - "completed_at": { + "created_at": { "type": "string", "format": "date-time", - "nullable": true - }, - "estimated_minutes": { - "type": "integer" - }, - "queue_position": { - "type": "integer", - "nullable": true - }, - "queue_approved_ahead": { - "type": "integer", - "nullable": true + "readOnly": true }, - "queue_awaiting_review_ahead": { - "type": "integer", - "nullable": true + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ - "accepted_at", - "completed_at", + "channel", "created_at", - "estimated_minutes", - "public_token", - "queue_approved_ahead", - "queue_awaiting_review_ahead", - "queue_position", - "started_at", - "status", - "title" + "credential_set", + "id", + "is_active", + "label", + "payload_version", + "scope", + "signing_secret_set", + "telegram_chat_id", + "updated_at" ] }, - "MemberRefund": { + "NotificationDestinationPayloadVersionEnum": { + "enum": [ + 1, + 2 + ], + "type": "integer", + "description": "* `1` - Legacy\n* `2` - Structured" + }, + "NotificationDestinationWrite": { "type": "object", - "description": "Money sent back on the member's own charge. Never names the staff member.", "properties": { - "amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "currency": { - "type": "string" - }, - "status": { - "type": "string" - }, - "reason": { - "type": "string" + "channel": { + "$ref": "#/components/schemas/Channel59dEnum" }, - "settled_at": { + "label": { "type": "string", - "format": "date-time", - "nullable": true - }, - "method": { - "type": "string" + "maxLength": 80 }, - "reference": { - "type": "string" + "webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2000 }, - "refunded_at": { + "telegram_chat_id": { "type": "string", - "format": "date-time", - "nullable": true - } - }, - "required": [ - "amount", - "currency", - "method", - "reason", - "reference", - "refunded_at", - "settled_at", - "status" - ] - }, - "MemberRegistrationCancelResponse": { - "type": "object", - "properties": { - "status": { - "allOf": [ + "maxLength": 64 + }, + "signing_secret": { + "writeOnly": true, + "oneOf": [ + { + "type": "string", + "maxLength": 512, + "minLength": 16 + }, { - "$ref": "#/components/schemas/MemberRegistrationCancelResponseStatusEnum" + "type": "string", + "maxLength": 0 } - ], - "readOnly": true + ] + }, + "is_active": { + "type": "boolean", + "default": true + }, + "payload_version": { + "$ref": "#/components/schemas/NotificationDestinationWritePayloadVersionEnum" + }, + "scope": { + "$ref": "#/components/schemas/DestinationScope" } }, "required": [ - "status" + "channel", + "label" ] }, - "MemberRegistrationCancelResponseStatusEnum": { + "NotificationDestinationWritePayloadVersionEnum": { "enum": [ - "cancelled" + 1, + 2 ], - "type": "string", - "description": "* `cancelled` - cancelled" + "type": "integer", + "description": "* `1` - 1\n* `2` - 2" }, - "MemberRequestHistory": { + "NotificationFeature": { "type": "object", "properties": { - "status": { - "type": "string" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "item_count": { - "type": "integer" - }, - "returned_quantity": { - "type": "integer" + "key": { + "$ref": "#/components/schemas/Key4feEnum" }, - "damaged_quantity": { - "type": "integer" + "label": { + "type": "string" }, - "missing_quantity": { - "type": "integer" + "events": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "created_at", - "damaged_quantity", - "item_count", - "missing_quantity", - "returned_quantity", - "status" + "events", + "key", + "label" ] }, - "MemberRetentionReport": { + "NotificationMarkAllRead": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemberRetentionRow" - } - }, - "meta": { - "type": "object", - "additionalProperties": {} - }, - "report_key": { - "type": "string" + "updated": { + "type": "integer" } }, "required": [ - "meta", - "report_key", - "rows", - "typed_rows" + "updated" ] }, - "MemberRetentionRow": { + "NotificationPreferenceCell": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, - "cohort_month": { - "type": "string" - }, - "months_after_join": { - "type": "integer" - }, - "cohort_members": { - "type": "integer" - }, - "eligible_members": { - "type": "integer" - }, - "retained_members": { - "type": "integer" + "feature": { + "$ref": "#/components/schemas/Feature4feEnum" }, - "retention_percent": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$", - "nullable": true + "channel": { + "$ref": "#/components/schemas/Channel292Enum" }, - "churned_members": { - "type": "integer" + "enabled": { + "type": "boolean" }, - "reactivated_members": { - "type": "integer" + "source": { + "$ref": "#/components/schemas/NotificationPreferenceCellSourceEnum" } }, "required": [ - "churned_members", - "cohort_members", - "cohort_month", - "eligible_members", - "months_after_join", - "reactivated_members", - "retained_members", - "retention_percent" + "channel", + "enabled", + "feature", + "source" ] }, - "MemberSettlement": { + "NotificationPreferenceCellSourceEnum": { + "enum": [ + "default", + "override" + ], + "type": "string", + "description": "* `default` - default\n* `override` - override" + }, + "NotificationPreferenceChange": { "type": "object", - "description": "The receipt for a charge staff settled in person, shown back to the payer.", "properties": { - "method": { - "type": "string" + "feature": { + "$ref": "#/components/schemas/Feature4feEnum" }, - "received_at": { - "type": "string", - "format": "date-time" + "channel": { + "$ref": "#/components/schemas/Channel292Enum" }, - "reference": { - "type": "string" + "enabled": { + "type": "boolean" } }, "required": [ - "method", - "received_at", - "reference" + "channel", + "enabled", + "feature" ] }, - "MemberSignUp": { + "NotificationRecipient": { "type": "object", "properties": { - "display_name": { - "type": "string", - "maxLength": 200 + "id": { + "type": "integer", + "readOnly": true }, - "email": { + "username": { "type": "string", - "format": "email", - "maxLength": 254 + "readOnly": true }, - "phone": { - "type": "string", - "default": "", - "maxLength": 32 + "email": { + "readOnly": true, + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "password": { + "role": { "type": "string", - "writeOnly": true, - "maxLength": 128 + "readOnly": true }, - "website": { - "type": "string", - "default": "" + "receives_notifications": { + "type": "boolean" } }, "required": [ - "display_name", "email", - "password" + "id", + "receives_notifications", + "role", + "username" ] }, - "MemberTerm": { + "NotificationRecipientUpdate": { "type": "object", "properties": { - "plan_name": { - "type": "string" - }, - "starts_at": { - "type": "string", - "format": "date-time" - }, - "ends_at": { - "type": "string", - "format": "date-time" - }, - "current": { - "type": "boolean" + "id": { + "type": "integer" }, - "blocks_borrowing": { + "receives_notifications": { "type": "boolean" } }, "required": [ - "blocks_borrowing", - "current", - "ends_at", - "plan_name", - "starts_at" + "id", + "receives_notifications" ] }, - "MemberVerificationAck": { + "NotificationRuleCatalogItem": { "type": "object", "properties": { - "detail": { + "stream": { + "type": "string" + }, + "audience": { "type": "string" + }, + "targets": { + "type": "array", + "items": { + "type": "string" + } + }, + "events": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "detail" + "audience", + "events", + "stream", + "targets" ] }, - "MemberVerificationRequest": { + "NotificationRuleChange": { "type": "object", "properties": { - "membership_id": { - "type": "integer", - "minimum": 1 + "target": { + "type": "string" }, - "verified": { + "stream": { + "type": "string" + }, + "event": { + "type": "string" + }, + "audience": { + "type": "string" + }, + "muted": { "type": "boolean" } }, "required": [ - "membership_id", - "verified" + "audience", + "event", + "muted", + "stream", + "target" ] }, - "MemberVerificationResponse": { + "NotificationRuleMute": { "type": "object", "properties": { - "membership_id": { - "type": "integer" + "target": { + "type": "string" }, - "display_name": { + "stream": { "type": "string" }, - "verified_at": { - "type": "string", - "format": "date-time", - "nullable": true + "event": { + "type": "string" + }, + "audience": { + "type": "string" } }, "required": [ - "display_name", - "membership_id", - "verified_at" + "audience", + "event", + "stream", + "target" ] }, - "MemberWaiverResponse": { + "NotificationRulesResponse": { "type": "object", "properties": { - "has_waiver": { - "type": "boolean" + "catalog": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationRuleCatalogItem" + } }, - "body": { - "type": "string" + "mutes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationRuleMute" + } }, - "version": { - "type": "string" + "channels": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationChannel" + } + }, + "features": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationFeature" + } + }, + "preferences": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationPreferenceCell" + } } }, "required": [ - "has_waiver" + "catalog", + "channels", + "features", + "mutes", + "preferences" ] }, - "MembershipAccount": { + "NotificationUnreadCount": { "type": "object", "properties": { - "status": { - "$ref": "#/components/schemas/MembershipAccountStatusEnum" - }, - "renewal_cancelled_at": { - "type": "string", - "format": "date-time", - "nullable": true + "count": { + "type": "integer" } }, "required": [ - "renewal_cancelled_at", - "status" + "count" ] }, - "MembershipAccountStatusEnum": { + "NullEnum": { "enum": [ - "active", - "revoked" - ], - "type": "string", - "description": "* `active` - active\n* `revoked` - revoked" - }, - "MembershipApprovalOutcome": { - "oneOf": [ - { - "$ref": "#/components/schemas/AdminMembership" - }, - { - "$ref": "#/components/schemas/MembershipRequest" - } + null ] }, - "MembershipCreate": { + "OccurrenceResult": { "type": "object", "properties": { - "username": { - "type": "string", - "maxLength": 150 - }, - "email": { - "oneOf": [ - { - "type": "string", - "format": "email", - "maxLength": 254 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "occurrence": { + "type": "integer" }, - "first_name": { + "starts_at": { "type": "string", - "maxLength": 150 + "format": "date-time" }, - "last_name": { + "ends_at": { "type": "string", - "maxLength": 150 + "format": "date-time" }, - "password": { - "type": "string", - "writeOnly": true + "booking_id": { + "type": "integer", + "nullable": true }, - "role_id": { - "type": "integer" + "status": { + "type": "string" + }, + "detail": { + "type": "string" } }, "required": [ - "role_id", - "username" + "booking_id", + "detail", + "ends_at", + "occurrence", + "starts_at", + "status" ] }, - "MembershipDispositionEnum": { - "enum": [ - "import_membership", - "no_membership" - ], - "type": "string", - "description": "* `import_membership` - Import membership\n* `no_membership` - Do not import membership" - }, - "MembershipList": { + "OfflineCheckInOperation": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "user": { - "allOf": [ - { - "$ref": "#/components/schemas/User" - } - ], - "readOnly": true - }, - "makerspace_id": { - "type": "integer", - "readOnly": true - }, - "makerspace_slug": { + "operation_id": { "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "format": "uuid" }, - "role": { - "allOf": [ - { - "$ref": "#/components/schemas/Role827Enum" - } - ], - "readOnly": true + "checkin_token": { + "type": "string", + "maxLength": 64 }, - "assigned_role": { - "allOf": [ - { - "$ref": "#/components/schemas/MembershipRoleSummary" - } - ], - "readOnly": true, - "nullable": true + "reported_occurred_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "checkin_token", + "operation_id", + "reported_occurred_at" + ] + }, + "OfflineCheckInResult": { + "type": "object", + "properties": { + "operation_id": { + "type": "string", + "format": "uuid" }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/Status37fEnum" - } - ], - "readOnly": true + "outcome": { + "$ref": "#/components/schemas/OfflineCheckInResultOutcomeEnum" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "registration_id": { + "type": "integer" }, - "payment": { - "allOf": [ - { - "$ref": "#/components/schemas/StaffPaymentSummary" - } - ], - "nullable": true, - "readOnly": true + "attended_at": { + "type": "string", + "format": "date-time" } }, "required": [ - "assigned_role", - "created_at", - "id", - "makerspace_id", - "makerspace_slug", - "payment", - "role", - "status", - "user" + "operation_id", + "outcome" ] }, - "MembershipListResponse": { + "OfflineCheckInResultOutcomeEnum": { + "enum": [ + "applied", + "duplicate_operation", + "already_attended", + "registration_changed", + "event_unavailable", + "invalid_token", + "outside_window" + ], + "type": "string", + "description": "* `applied` - applied\n* `duplicate_operation` - duplicate_operation\n* `already_attended` - already_attended\n* `registration_changed` - registration_changed\n* `event_unavailable` - event_unavailable\n* `invalid_token` - invalid_token\n* `outside_window` - outside_window" + }, + "OfflineCheckInSyncRequest": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { + "lease_token": { "type": "string", - "format": "uri", - "nullable": true + "maxLength": 8192 }, - "previous": { + "operations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OfflineCheckInOperation" + } + } + }, + "required": [ + "lease_token", + "operations" + ] + }, + "OfflineCheckInSyncResponse": { + "type": "object", + "properties": { + "recorded_at": { "type": "string", - "format": "uri", - "nullable": true + "format": "date-time" }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/AdminMembership" + "$ref": "#/components/schemas/OfflineCheckInResult" } } }, "required": [ - "count", - "next", - "previous", + "recorded_at", "results" ] }, - "MembershipOutcome": { + "OfflineRosterEvent": { "type": "object", "properties": { - "outcome": { - "$ref": "#/components/schemas/MembershipOutcomeOutcomeEnum" - }, - "membership_id": { - "type": "integer" - }, - "request_id": { + "id": { "type": "integer" }, - "state": { + "title": { "type": "string" + }, + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" } }, "required": [ - "outcome", - "state" + "ends_at", + "id", + "starts_at", + "title" ] }, - "MembershipOutcomeOutcomeEnum": { - "enum": [ - "joined", - "requested" - ], - "type": "string", - "description": "* `joined` - joined\n* `requested` - requested" - }, - "MembershipPlan": { + "OfflineRosterRegistration": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "registration_id": { + "type": "integer" }, - "name": { + "checkin_token": { "type": "string", - "maxLength": 120 + "format": "uuid" }, - "interval": { - "$ref": "#/components/schemas/IntervalEnum" + "name": { + "type": "string" }, - "custom_days": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true + "host_waiver_state": { + "$ref": "#/components/schemas/HostWaiverStateEnum" + } + }, + "required": [ + "checkin_token", + "host_waiver_state", + "name", + "registration_id" + ] + }, + "OfflineRosterResponse": { + "type": "object", + "properties": { + "lease_token": { + "type": "string" }, - "amount": { + "lease_id": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "uuid" }, - "currency": { + "server_time": { "type": "string", - "maxLength": 3 - }, - "is_active": { - "type": "boolean" + "format": "date-time" }, - "created_at": { + "issued_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" }, - "updated_at": { + "expires_at": { "type": "string", - "format": "date-time", - "readOnly": true - }, - "loan_max_active": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true - }, - "loan_max_items": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true + "format": "date-time" }, - "auto_renew": { - "type": "boolean" + "scan_opens_at": { + "type": "string", + "format": "date-time" }, - "renewal_open_days": { - "type": "integer", - "maximum": 365, - "minimum": 0 + "scan_closes_at": { + "type": "string", + "format": "date-time" }, - "renewal_grace_days": { - "type": "integer", - "maximum": 365, - "minimum": 0 + "sync_deadline": { + "type": "string", + "format": "date-time" }, - "renewal_retries": { - "type": "integer", - "maximum": 3, - "minimum": 0 + "event": { + "$ref": "#/components/schemas/OfflineRosterEvent" }, - "reminder_days_before": {}, - "reminder_days_after": {} + "registrations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OfflineRosterRegistration" + } + } }, "required": [ - "created_at", - "id", - "interval", - "name", - "updated_at" + "event", + "expires_at", + "issued_at", + "lease_id", + "lease_token", + "registrations", + "scan_closes_at", + "scan_opens_at", + "server_time", + "sync_deadline" ] }, - "MembershipPolicyEnum": { - "enum": [ - "request", - "open", - "invite_only" - ], - "type": "string", - "description": "* `request` - Request\n* `open` - Open\n* `invite_only` - Invite only" - }, - "MembershipReason": { + "OidcBrowserCallback": { "type": "object", "properties": { - "reason": { + "code": { "type": "string", - "maxLength": 1000 + "maxLength": 4096 + }, + "state": { + "type": "string", + "maxLength": 512 + }, + "nonce": { + "type": "string", + "maxLength": 512 } }, "required": [ - "reason" + "code", + "nonce", + "state" ] }, - "MembershipRequest": { + "OidcBrowserLoginResponse": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "kind": { - "$ref": "#/components/schemas/MembershipRequestKindEnum" - }, - "state": { - "$ref": "#/components/schemas/State9efEnum" - }, - "invite_email": { - "type": "string", - "maxLength": 254 + "access": { + "type": "string" }, "user": { - "allOf": [ - { - "$ref": "#/components/schemas/MembershipRequestUser" - } - ], - "nullable": true, - "readOnly": true + "type": "object", + "additionalProperties": {} }, - "assigned_role": { + "outcome": { + "type": "string" + } + }, + "required": [ + "access", + "outcome", + "user" + ] + }, + "OidcBrowserStart": { + "type": "object", + "properties": { + "surface": { "allOf": [ { - "$ref": "#/components/schemas/MembershipRequestRole" + "$ref": "#/components/schemas/Surface356Enum" } ], - "nullable": true, - "readOnly": true - }, - "decision_note": { - "type": "string" - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "default": "member" }, - "decided_at": { + "redirect_uri": { "type": "string", - "format": "date-time", - "nullable": true - }, - "application_answers": { - "nullable": true, - "readOnly": true - }, - "vouches": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Vouch" - } + "format": "uri", + "maxLength": 2048 }, - "admission_payment": { - "allOf": [ + "email": { + "oneOf": [ { - "$ref": "#/components/schemas/AdmissionPayment" + "type": "string", + "format": "email" + }, + { + "type": "string", + "maxLength": 0 } - ], - "nullable": true, - "readOnly": true + ] }, - "expires_at": { + "makerspace_slug": { + "oneOf": [ + { + "type": "string", + "pattern": "^[-a-zA-Z0-9_]+$" + }, + { + "type": "string", + "maxLength": 0 + } + ] + } + }, + "required": [ + "redirect_uri" + ] + }, + "OidcBrowserStartResponse": { + "type": "object", + "properties": { + "authorization_url": { "type": "string", - "format": "date-time", - "nullable": true + "format": "uri" + }, + "state": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "expires_in": { + "type": "integer" } }, "required": [ - "admission_payment", - "application_answers", - "assigned_role", - "created_at", - "id", - "kind", - "state", - "user", - "vouches" + "authorization_url", + "expires_in", + "nonce", + "state" ] }, - "MembershipRequestCreate": { + "OperatorCandidate": { "type": "object", "properties": { - "application_answers": { - "nullable": true + "user_id": { + "type": "integer", + "readOnly": true }, - "website": { + "username": { "type": "string", - "writeOnly": true + "readOnly": true + }, + "display_name": { + "type": "string", + "readOnly": true } - } + }, + "required": [ + "display_name", + "user_id", + "username" + ] }, - "MembershipRequestKindEnum": { + "OperatorEnum": { "enum": [ - "request", - "invite" + "gt", + "gte", + "lt", + "lte", + "eq" ], "type": "string", - "description": "* `request` - Request\n* `invite` - Invite" + "description": "* `gt` - gt\n* `gte` - gte\n* `lt` - lt\n* `lte` - lte\n* `eq` - eq" }, - "MembershipRequestListResponse": { + "OrganizationCreate": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { + "name": { "type": "string", - "format": "uri", - "nullable": true + "maxLength": 200 }, - "previous": { + "slug": { "type": "string", - "format": "uri", - "nullable": true + "maxLength": 50, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MembershipRequest" - } + "description": { + "type": "string" + }, + "website": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "public_profile_enabled": { + "type": "boolean" } }, "required": [ - "count", - "next", - "previous", - "results" + "name", + "slug" ] }, - "MembershipRequestRole": { + "OrganizationCreationPolicy": { "type": "object", "properties": { - "id": { + "enabled": { + "type": "boolean" + }, + "eligible": { + "type": "boolean" + }, + "max_owned": { "type": "integer" }, - "name": { + "max_per_day": { + "type": "integer" + }, + "reason": { "type": "string" } }, "required": [ - "id", - "name" + "eligible", + "enabled", + "max_owned", + "max_per_day", + "reason" ] }, - "MembershipRequestUser": { + "OrganizationDetail": { "type": "object", "properties": { "id": { - "type": "integer" + "type": "integer", + "readOnly": true }, - "username": { - "type": "string" + "slug": { + "type": "string", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "email": { + "name": { "type": "string", - "format": "email" - } - }, - "required": [ - "email", - "id", - "username" - ] - }, - "MembershipRevenueReport": { - "type": "object", - "properties": { - "rows": { + "readOnly": true + }, + "is_active": { + "type": "boolean", + "readOnly": true + }, + "governance_actions": { "type": "array", "items": { - "type": "array", - "items": {} - } + "type": "string" + }, + "readOnly": true }, - "typed_rows": { + "granted_actions": { "type": "array", "items": { - "$ref": "#/components/schemas/MembershipRevenueRow" - } - }, - "meta": { - "type": "object", - "additionalProperties": {} + "type": "string" + }, + "readOnly": true }, - "report_key": { - "type": "string" - } - }, - "required": [ - "meta", - "report_key", - "rows", - "typed_rows" - ] - }, - "MembershipRevenueRow": { - "type": "object", - "properties": { - "makerspace_id": { - "type": "integer" + "description": { + "type": "string", + "readOnly": true }, - "period": { + "website": { "type": "string", - "format": "date" + "format": "uri", + "readOnly": true }, - "currency": { - "type": "string" + "logo_url": { + "type": "string", + "format": "uri", + "nullable": true, + "readOnly": true }, - "recurring_members": { - "type": "integer" + "public_profile_enabled": { + "type": "boolean", + "readOnly": true }, - "mrr": { + "legal_name": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + "readOnly": true }, - "arr": { + "registration_number": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + "readOnly": true }, - "arpu": { + "contact_email": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" - }, - "churned_members": { - "type": "integer" + "format": "email", + "readOnly": true }, - "reactivated_members": { - "type": "integer" + "billing_email": { + "type": "string", + "format": "email", + "readOnly": true } }, "required": [ - "arpu", - "arr", - "churned_members", - "currency", - "mrr", - "period", - "reactivated_members", - "recurring_members" + "billing_email", + "contact_email", + "description", + "governance_actions", + "granted_actions", + "id", + "is_active", + "legal_name", + "logo_url", + "name", + "public_profile_enabled", + "registration_number", + "slug", + "website" ] }, - "MembershipRoleSummary": { + "OrganizationEventHost": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "name": { - "type": "string", - "readOnly": true - }, "slug": { "type": "string", "readOnly": true, "pattern": "^[-a-zA-Z0-9_]+$" }, - "legacy_role": { - "readOnly": true, - "nullable": true, - "oneOf": [ - { - "$ref": "#/components/schemas/LegacyRoleEnum" - }, - { - "$ref": "#/components/schemas/NullEnum" - } - ] - }, - "is_default": { - "type": "boolean", + "name": { + "type": "string", "readOnly": true }, - "is_protected": { - "type": "boolean", + "logo_url": { + "type": "string", + "format": "uri", + "nullable": true, "readOnly": true } }, "required": [ - "id", - "is_default", - "is_protected", - "legacy_role", + "logo_url", "name", "slug" ] }, - "MembershipTerm": { + "OrganizationInvitation": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "membership": { + "organization_id": { "type": "integer", "readOnly": true }, - "plan": { - "type": "integer", + "governance_actions": { "readOnly": true }, - "plan_name": { - "type": "string", + "granted_actions": { "readOnly": true }, - "starts_at": { + "expires_at": { "type": "string", "format": "date-time", "readOnly": true }, - "ends_at": { + "redeemed_at": { "type": "string", "format": "date-time", - "readOnly": true + "readOnly": true, + "nullable": true }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/MembershipTermStatusEnum" - } - ], - "readOnly": true + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true }, - "renewal_payment": { + "created_by_id": { "type": "integer", "readOnly": true, "nullable": true }, - "created_by": { + "redeemed_by_id": { "type": "integer", "readOnly": true, "nullable": true @@ -86147,685 +108381,662 @@ "type": "string", "format": "date-time", "readOnly": true + }, + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/State31eEnum" + } + ], + "readOnly": true } }, "required": [ "created_at", - "created_by", - "ends_at", + "created_by_id", + "expires_at", + "governance_actions", + "granted_actions", "id", - "membership", - "plan", - "plan_name", - "renewal_payment", - "starts_at", - "status" + "organization_id", + "redeemed_at", + "redeemed_by_id", + "revoked_at", + "state" ] }, - "MembershipTermCreate": { + "OrganizationInvitationCreate": { "type": "object", "properties": { - "plan_id": { - "type": "integer" + "governance_actions": { + "type": "array", + "items": { + "type": "string" + } }, - "starts_at": { - "type": "string", - "format": "date-time", - "nullable": true - } - }, - "required": [ - "plan_id" - ] - }, - "MembershipTermStatusEnum": { - "enum": [ - "active", - "expired", - "cancelled" - ], - "type": "string", - "description": "* `active` - Active\n* `expired` - Expired\n* `cancelled` - Cancelled" - }, - "Method2edEnum": { - "enum": [ - "cash", - "upi", - "bank_transfer", - "card_machine", - "cheque", - "other" - ], - "type": "string", - "description": "* `cash` - Cash\n* `upi` - UPI\n* `bank_transfer` - Bank transfer\n* `card_machine` - Card machine\n* `cheque` - Cheque\n* `other` - Other" - }, - "MigrationExportCreate": { - "type": "object", - "properties": { - "approval_id": { - "type": "string", - "format": "uuid" + "granted_actions": { + "type": "array", + "items": { + "type": "string" + } }, - "target_age_recipient": { - "type": "string", - "writeOnly": true, - "maxLength": 256 + "expires_in_days": { + "type": "integer", + "maximum": 30, + "minimum": 1, + "default": 7 } - }, - "required": [ - "approval_id", - "target_age_recipient" - ] + } }, - "MigrationExportJob": { + "OrganizationInvitationCreated": { "type": "object", "properties": { "id": { - "type": "string", - "format": "uuid", + "type": "integer", "readOnly": true }, - "status": { - "$ref": "#/components/schemas/StatusE1dEnum" - }, - "failure_code": { - "oneOf": [ - { - "$ref": "#/components/schemas/FailureCodeEnum" - }, - { - "$ref": "#/components/schemas/BlankEnum" - } - ] - }, - "failure_detail": { - "type": "string", - "maxLength": 500 - }, - "manifest": {}, - "closure_digest": { - "type": "string" - }, - "archive_digest": { - "type": "string" + "organization_id": { + "type": "integer", + "readOnly": true }, - "format_version": { - "type": "integer" + "governance_actions": { + "readOnly": true }, - "source_retention_notice": { - "type": "string", + "granted_actions": { "readOnly": true }, - "created_at": { + "expires_at": { "type": "string", "format": "date-time", "readOnly": true }, - "started_at": { + "redeemed_at": { "type": "string", "format": "date-time", + "readOnly": true, "nullable": true }, - "completed_at": { + "revoked_at": { "type": "string", "format": "date-time", + "readOnly": true, "nullable": true }, - "expires_at": { + "created_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "redeemed_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { "type": "string", - "format": "date-time" + "format": "date-time", + "readOnly": true + }, + "state": { + "allOf": [ + { + "$ref": "#/components/schemas/State31eEnum" + } + ], + "readOnly": true + }, + "token": { + "type": "string", + "readOnly": true + }, + "redeem_path": { + "type": "string", + "readOnly": true } }, "required": [ - "archive_digest", - "closure_digest", "created_at", + "created_by_id", "expires_at", - "format_version", + "governance_actions", + "granted_actions", "id", - "source_retention_notice" + "organization_id", + "redeem_path", + "redeemed_at", + "redeemed_by_id", + "revoked_at", + "state", + "token" ] }, - "MobilePaymentIntentResponse": { + "OrganizationInvitationList": { "type": "object", "properties": { - "payment_id": { + "count": { "type": "integer" }, - "client_secret": { - "type": "string" - }, - "publishable_key": { - "type": "string" - }, - "customer_id": { + "next": { "type": "string", "nullable": true }, - "ephemeral_key": { + "previous": { "type": "string", "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrganizationInvitation" + } } }, "required": [ - "client_secret", - "payment_id", - "publishable_key" + "count", + "next", + "previous", + "results" ] }, - "Mode087Enum": { - "enum": [ - "preview", - "apply" - ], - "type": "string", - "description": "* `preview` - Preview\n* `apply` - Apply" - }, - "ModuleAction": { + "OrganizationInvitationRedeem": { "type": "object", - "description": "The one field either mutation takes.\n\nA real class rather than `inline_serializer`, which returns an *instance* and so\ncannot be called with `data=` at request time.", "properties": { - "key": { - "type": "string" + "token": { + "type": "string", + "maxLength": 200, + "minLength": 20 } }, "required": [ - "key" + "token" ] }, - "ModulesEnum": { - "enum": [ - "asset_units", - "backup", - "bookings", - "bulk_import", - "containers", - "devices", - "discord", - "email", - "events", - "evidence_uploads", - "guest_handover", - "machine_service", - "machines", - "maintenance", - "mattermost", - "member_accounts", + "OrganizationInvitationRedeemed": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/AuthUserPayload" + }, + "membership": { + "$ref": "#/components/schemas/OrganizationMembership" + } + }, + "required": [ "membership", - "mobile", - "notifications", - "payments", - "printing", - "procurement", - "public_inventory", - "qr_management", - "qr_print_batches", - "reports", - "request_workflow", - "scanner", - "slack", - "staff_admin", - "stock_transfers", - "stocktake", - "telegram", - "webhook" - ], - "type": "string", - "description": "* `asset_units` - asset_units\n* `backup` - backup\n* `bookings` - bookings\n* `bulk_import` - bulk_import\n* `containers` - containers\n* `devices` - devices\n* `discord` - discord\n* `email` - email\n* `events` - events\n* `evidence_uploads` - evidence_uploads\n* `guest_handover` - guest_handover\n* `machine_service` - machine_service\n* `machines` - machines\n* `maintenance` - maintenance\n* `mattermost` - mattermost\n* `member_accounts` - member_accounts\n* `membership` - membership\n* `mobile` - mobile\n* `notifications` - notifications\n* `payments` - payments\n* `printing` - printing\n* `procurement` - procurement\n* `public_inventory` - public_inventory\n* `qr_management` - qr_management\n* `qr_print_batches` - qr_print_batches\n* `reports` - reports\n* `request_workflow` - request_workflow\n* `scanner` - scanner\n* `slack` - slack\n* `staff_admin` - staff_admin\n* `stock_transfers` - stock_transfers\n* `stocktake` - stocktake\n* `telegram` - telegram\n* `webhook` - webhook" + "user" + ] }, - "MostLentReport": { + "OrganizationLink": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "id": { + "type": "integer", + "readOnly": true }, - "typed_rows": { + "makerspace_id": { + "type": "integer", + "readOnly": true + }, + "makerspace_name": { + "type": "string", + "readOnly": true + }, + "relationship": { + "$ref": "#/components/schemas/RelationshipEnum" + } + }, + "required": [ + "id", + "makerspace_id", + "makerspace_name", + "relationship" + ] + }, + "OrganizationLinkPage": { + "type": "object", + "properties": { + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/MostLentReportRow" + "$ref": "#/components/schemas/OrganizationLink" } } }, "required": [ - "rows", - "typed_rows" + "count", + "next", + "previous", + "results" + ] + }, + "OrganizationLinkWrite": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer", + "minimum": 1 + }, + "relationship": { + "$ref": "#/components/schemas/RelationshipEnum" + } + }, + "required": [ + "makerspace_id", + "relationship" ] }, - "MostLentReportRow": { + "OrganizationList": { "type": "object", "properties": { - "makerspace_id": { + "count": { "type": "integer" }, - "product_name": { - "type": "string" + "next": { + "type": "string", + "nullable": true }, - "times_lent": { - "type": "integer" + "previous": { + "type": "string", + "nullable": true }, - "total_quantity_lent": { - "type": "integer" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrganizationSummary" + } } }, "required": [ - "product_name", - "times_lent", - "total_quantity_lent" + "count", + "next", + "previous", + "results" ] }, - "MoveToInventoryRequest": { + "OrganizationMembership": { "type": "object", "properties": { - "mode": { - "$ref": "#/components/schemas/MoveToInventoryRequestModeEnum" - }, - "product_id": { - "type": "integer", - "nullable": true - }, - "quantity": { + "id": { "type": "integer", - "minimum": 1 + "readOnly": true }, - "box": { + "user_id": { "type": "integer", - "nullable": true + "readOnly": true }, - "category": { - "type": "integer", - "nullable": true + "username": { + "type": "string", + "readOnly": true }, - "tracking_mode": { - "allOf": [ - { - "$ref": "#/components/schemas/MoveToInventoryRequestTrackingModeEnum" - } - ], - "default": "quantity" + "display_name": { + "type": "string", + "readOnly": true }, - "is_public": { - "type": "boolean", - "default": true + "email": { + "type": "string", + "format": "email", + "readOnly": true }, - "public_availability_mode": { + "status": { "allOf": [ { - "$ref": "#/components/schemas/MoveToInventoryRequestPublicAvailabilityModeEnum" + "$ref": "#/components/schemas/StatusE75Enum" } ], - "default": "status_only" + "readOnly": true }, - "show_public_count": { - "type": "boolean", - "default": false + "governance_actions": { + "readOnly": true }, - "public_self_checkout_enabled": { - "type": "boolean", - "default": false + "granted_actions": { + "readOnly": true }, - "name": { + "created_at": { "type": "string", - "maxLength": 200 + "format": "date-time", + "readOnly": true }, - "description": { + "updated_at": { "type": "string", - "default": "" + "format": "date-time", + "readOnly": true } }, "required": [ - "mode", - "quantity" + "created_at", + "display_name", + "email", + "governance_actions", + "granted_actions", + "id", + "status", + "updated_at", + "user_id", + "username" ] }, - "MoveToInventoryRequestModeEnum": { - "enum": [ - "create", - "topup" - ], - "type": "string", - "description": "* `create` - create\n* `topup` - topup" - }, - "MoveToInventoryRequestPublicAvailabilityModeEnum": { - "enum": [ - "exact_count", - "status_only", - "hidden" - ], - "type": "string", - "description": "* `exact_count` - exact_count\n* `status_only` - status_only\n* `hidden` - hidden" - }, - "MoveToInventoryRequestTrackingModeEnum": { - "enum": [ - "quantity", - "individual" - ], - "type": "string", - "description": "* `quantity` - quantity\n* `individual` - individual" - }, - "MoveToPrintingRequest": { + "OrganizationMembershipList": { "type": "object", "properties": { - "target": { - "$ref": "#/components/schemas/TargetEnum" - }, - "printer": { - "type": "integer", - "nullable": true - }, - "material": { - "type": "string", - "maxLength": 100 - }, - "color": { - "type": "string", - "maxLength": 100 - }, - "brand": { - "type": "string", - "maxLength": 100 - }, - "lot_code": { - "type": "string", - "maxLength": 100 - }, - "initial_weight_grams": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,6}(?:\\.\\d{0,2})?$" - }, - "remaining_weight_grams": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,6}(?:\\.\\d{0,2})?$" - }, - "is_active": { - "type": "boolean" + "count": { + "type": "integer" }, - "opened_at": { + "next": { "type": "string", - "format": "date-time", "nullable": true }, - "name": { - "type": "string", - "maxLength": 200 - }, - "model": { + "previous": { "type": "string", - "maxLength": 200 - }, - "status": { - "$ref": "#/components/schemas/MoveToPrintingRequestStatusEnum" + "nullable": true }, - "notes": { - "type": "string" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OrganizationMembership" + } } }, "required": [ - "target" + "count", + "next", + "previous", + "results" ] }, - "MoveToPrintingRequestStatusEnum": { - "enum": [ - "active", - "maintenance", - "offline" - ], - "type": "string", - "description": "* `active` - active\n* `maintenance` - maintenance\n* `offline` - offline" - }, - "MyMembershipRequest": { + "OrganizationReportBreakdown": { "type": "object", "properties": { - "id": { - "type": "integer" - }, - "makerspace": { - "type": "object", - "additionalProperties": {} - }, - "state": { - "type": "string" - }, - "kind": { - "type": "string" - }, - "admission_payment": { - "type": "object", - "additionalProperties": {}, - "nullable": true + "rows": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": {} + } }, - "expires_at": { - "type": "string", - "format": "date-time", - "nullable": true + "makerspace_id": { + "type": "integer" } }, "required": [ - "admission_payment", - "expires_at", - "id", - "kind", - "makerspace", - "state" + "makerspace_id", + "rows" ] }, - "MyMembershipRow": { + "OrganizationReportResponse": { "type": "object", "properties": { - "makerspace": { - "type": "object", - "additionalProperties": {} - }, - "membership_status": { + "report_key": { "type": "string" }, - "role": { - "type": "string" + "strategy": { + "$ref": "#/components/schemas/StrategyEnum" }, - "actions": { + "breakdown": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/OrganizationReportBreakdown" } }, - "can_refer": { - "type": "boolean" - }, - "can_verify": { - "type": "boolean" + "total": { + "$ref": "#/components/schemas/OrganizationReportRows" }, - "verified_at": { - "type": "string", - "format": "date-time", + "chart_spec": { + "allOf": [ + { + "$ref": "#/components/schemas/ReportChartSpec" + } + ], "nullable": true - }, - "referrals_enabled": { - "type": "boolean" - }, - "waiver_accepted": { - "type": "boolean" - }, - "waiver_acceptance_required": { - "type": "boolean" } }, "required": [ - "actions", - "can_refer", - "can_verify", - "makerspace", - "membership_status", - "referrals_enabled", - "role", - "verified_at", - "waiver_acceptance_required", - "waiver_accepted" + "breakdown", + "report_key", + "strategy", + "total" ] }, - "MyMemberships": { + "OrganizationReportRows": { "type": "object", "properties": { - "memberships": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MyMembershipRow" - } - }, - "requests": { + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/MyMembershipRequest" + "type": "object", + "additionalProperties": {} } } }, "required": [ - "memberships", - "requests" + "rows" ] }, - "NeedsFixAction": { + "OrganizationSelfCreate": { "type": "object", "properties": { - "action": { - "$ref": "#/components/schemas/NeedsFixActionActionEnum" + "name": { + "type": "string", + "maxLength": 200 }, - "quantity": { - "type": "integer", - "minimum": 1 + "slug": { + "type": "string", + "maxLength": 50, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "description": { + "type": "string", + "maxLength": 10000 + }, + "website": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + { + "type": "string", + "maxLength": 0 + } + ] } }, "required": [ - "action", - "quantity" + "name", + "slug" ] }, - "NeedsFixActionActionEnum": { - "enum": [ - "repair", - "scrap", - "shelve" - ], - "type": "string", - "description": "* `repair` - repair\n* `scrap` - scrap\n* `shelve` - shelve" - }, - "Notification": { + "OrganizationSummary": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "level": { - "allOf": [ - { - "$ref": "#/components/schemas/LevelEnum" - } - ], - "readOnly": true - }, - "event": { + "slug": { "type": "string", - "readOnly": true + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "title": { + "name": { "type": "string", "readOnly": true }, - "body": { - "type": "string", + "is_active": { + "type": "boolean", "readOnly": true }, - "url_path": { - "type": "string", + "governance_actions": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true }, - "read_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time", + "granted_actions": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true } }, "required": [ - "body", - "created_at", - "event", + "governance_actions", + "granted_actions", "id", - "level", - "read_at", - "title", - "url_path" + "is_active", + "name", + "slug" ] }, - "NotificationChannel": { + "OrientationEnum": { + "enum": [ + "portrait", + "landscape" + ], + "type": "string", + "description": "* `portrait` - portrait\n* `landscape` - landscape" + }, + "OtpResetPasswordConfirm": { "type": "object", "properties": { - "key": { - "$ref": "#/components/schemas/KeyB30Enum" + "email": { + "type": "string", + "format": "email" }, - "label": { - "type": "string" + "code": { + "type": "string", + "writeOnly": true, + "pattern": "^\\d{6}$" + }, + "new_password": { + "type": "string", + "writeOnly": true } }, "required": [ - "key", - "label" + "code", + "email", + "new_password" ] }, - "NotificationDestination": { + "OutcomeA1aEnum": { + "enum": [ + "succeeded", + "retryable_failed", + "failed" + ], + "type": "string", + "description": "* `succeeded` - succeeded\n* `retryable_failed` - retryable_failed\n* `failed` - failed" + }, + "OverdueSchedule": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "channel": { - "allOf": [ - { - "$ref": "#/components/schemas/Channel59dEnum" - } - ], + "machine_id": { + "type": "integer", "readOnly": true }, - "label": { + "machine_name": { "type": "string", "readOnly": true }, - "telegram_chat_id": { + "description": { + "type": "string", + "readOnly": true + }, + "next_due": { + "type": "string", + "format": "date", + "readOnly": true + } + }, + "required": [ + "description", + "id", + "machine_id", + "machine_name", + "next_due" + ] + }, + "OwnMemberCard": { + "type": "object", + "description": "Never carries the photo key or a URL that outlives the response.", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "card_number": { + "type": "integer", + "readOnly": true + }, + "printed_name": { "type": "string", "readOnly": true }, + "membership_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, "is_active": { "type": "boolean", "readOnly": true }, - "payload_version": { - "allOf": [ - { - "$ref": "#/components/schemas/NotificationDestinationPayloadVersionEnum" - } - ], + "photo_set": { + "type": "boolean", + "readOnly": true + }, + "photo_consent_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "qr_active": { + "type": "boolean", + "readOnly": true + }, + "print_count": { + "type": "integer", "readOnly": true }, - "credential_set": { + "last_printed_at": { "type": "string", - "readOnly": true + "format": "date-time", + "readOnly": true, + "nullable": true }, - "signing_secret_set": { + "issued_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "scope": { + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_reason": { "type": "string", "readOnly": true }, @@ -86838,2083 +109049,2263 @@ "type": "string", "format": "date-time", "readOnly": true + }, + "qr_image": { + "type": "string", + "nullable": true, + "readOnly": true } }, "required": [ - "channel", + "card_number", "created_at", - "credential_set", "id", "is_active", - "label", - "payload_version", - "scope", - "signing_secret_set", - "telegram_chat_id", + "issued_at", + "last_printed_at", + "membership_id", + "photo_consent_at", + "photo_set", + "print_count", + "printed_name", + "qr_active", + "qr_image", + "revoked_at", + "revoked_reason", "updated_at" ] }, - "NotificationDestinationPayloadVersionEnum": { - "enum": [ - 1, - 2 - ], - "type": "integer", - "description": "* `1` - Legacy\n* `2` - Structured" - }, - "NotificationDestinationWrite": { + "PackBookingRules": { "type": "object", + "description": "Query-free shape: bookings may be tombstoned on this deployment.", "properties": { - "channel": { - "$ref": "#/components/schemas/Channel59dEnum" - }, - "label": { - "type": "string", - "maxLength": 80 - }, - "webhook_url": { + "name": { "type": "string", - "writeOnly": true, - "maxLength": 2000 + "maxLength": 200 }, - "telegram_chat_id": { - "type": "string", - "maxLength": 64 + "min_booking_duration_minutes": { + "type": "integer", + "minimum": 1 }, - "signing_secret": { - "writeOnly": true, - "oneOf": [ - { - "type": "string", - "maxLength": 512, - "minLength": 16 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "max_booking_duration_minutes": { + "type": "integer", + "minimum": 1 }, - "is_active": { - "type": "boolean", - "default": true + "booking_lead_time_minutes": { + "type": "integer", + "minimum": 0 }, - "payload_version": { - "$ref": "#/components/schemas/NotificationDestinationWritePayloadVersionEnum" + "max_booking_advance_days": { + "type": "integer", + "minimum": 1 }, - "scope": { - "$ref": "#/components/schemas/DestinationScope" + "approval_mode": { + "$ref": "#/components/schemas/PackBookingRulesApprovalModeEnum" } }, "required": [ - "channel", - "label" + "approval_mode", + "booking_lead_time_minutes", + "max_booking_advance_days", + "max_booking_duration_minutes", + "min_booking_duration_minutes", + "name" ] }, - "NotificationDestinationWritePayloadVersionEnum": { + "PackBookingRulesApprovalModeEnum": { "enum": [ - 1, - 2 + "instant", + "approve" ], - "type": "integer", - "description": "* `1` - 1\n* `2` - 2" + "type": "string", + "description": "* `instant` - instant\n* `approve` - approve" }, - "NotificationFeature": { + "PackCategory": { "type": "object", "properties": { - "key": { - "$ref": "#/components/schemas/Key4feEnum" + "slug": { + "type": "string", + "maxLength": 120, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "label": { - "type": "string" + "name": { + "type": "string", + "maxLength": 100 }, - "events": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "events", - "key", - "label" - ] - }, - "NotificationMarkAllRead": { - "type": "object", - "properties": { - "updated": { - "type": "integer" + "display_order": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "icon": { + "type": "string", + "maxLength": 50 + }, + "loan_days": { + "type": "integer", + "maximum": 365, + "minimum": 1, + "nullable": true + }, + "max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "nullable": true } }, "required": [ - "updated" + "display_order", + "icon", + "loan_days", + "max_renewals", + "name", + "slug" ] }, - "NotificationPreferenceCell": { + "PackChange": { "type": "object", "properties": { - "feature": { - "$ref": "#/components/schemas/Feature4feEnum" + "section": { + "type": "string" }, - "channel": { - "$ref": "#/components/schemas/ChannelB30Enum" + "key": { + "type": "string" }, - "enabled": { - "type": "boolean" + "before": { + "nullable": true }, - "source": { - "$ref": "#/components/schemas/NotificationPreferenceCellSourceEnum" + "after": { + "nullable": true } }, "required": [ - "channel", - "enabled", - "feature", - "source" + "after", + "before", + "key", + "section" ] }, - "NotificationPreferenceCellSourceEnum": { - "enum": [ - "default", - "override" - ], - "type": "string", - "description": "* `default` - default\n* `override` - override" - }, - "NotificationPreferenceChange": { + "PackChatTemplate": { "type": "object", "properties": { "feature": { "$ref": "#/components/schemas/Feature4feEnum" }, - "channel": { - "$ref": "#/components/schemas/ChannelB30Enum" + "event": { + "type": "string", + "maxLength": 64 }, - "enabled": { + "text_body": { + "type": "string" + }, + "is_active": { "type": "boolean" } }, "required": [ - "channel", - "enabled", - "feature" + "event", + "feature", + "is_active", + "text_body" ] }, - "NotificationRecipient": { + "PackConfiguration": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "modules": { + "type": "array", + "items": { + "type": "string" + } }, - "username": { - "type": "string", - "readOnly": true + "features": { + "type": "array", + "items": { + "type": "string" + } }, - "email": { - "readOnly": true, - "oneOf": [ + "settings": { + "$ref": "#/components/schemas/PackSettings" + }, + "opening_hours": { + "$ref": "#/components/schemas/PackOpeningHours" + }, + "active_waiver": { + "allOf": [ { - "type": "string", - "format": "email" - }, + "$ref": "#/components/schemas/PackMakerspaceWaiver" + } + ], + "nullable": true + }, + "loan_policy": { + "allOf": [ { - "type": "string", - "maxLength": 0 + "$ref": "#/components/schemas/PackMakerspaceLoanPolicy" } - ] + ], + "nullable": true }, - "role": { - "type": "string", - "readOnly": true + "membership_plans": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PackMembershipPlan" + } }, - "receives_notifications": { - "type": "boolean" - } - }, - "required": [ - "email", - "id", - "receives_notifications", - "role", - "username" - ] - }, - "NotificationRecipientUpdate": { - "type": "object", - "properties": { - "id": { - "type": "integer" + "admission_policy": { + "allOf": [ + { + "$ref": "#/components/schemas/PackMakerspaceMembershipSettings" + } + ], + "nullable": true }, - "receives_notifications": { - "type": "boolean" - } - }, - "required": [ - "id", - "receives_notifications" - ] - }, - "NotificationRuleCatalogItem": { - "type": "object", - "properties": { - "stream": { - "type": "string" + "booking_rules": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PackBookingRules" + } }, - "audience": { - "type": "string" + "notification_defaults": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PackNotificationPreference" + } }, - "targets": { + "email_mutes": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/PackEmailNotificationMute" } }, - "events": { + "email_templates": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/PackEmailTemplate" + } + }, + "chat_templates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PackChatTemplate" + } + }, + "categories": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PackCategory" + } + }, + "item_templates": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PackInventoryProduct" } } }, "required": [ - "audience", - "events", - "stream", - "targets" + "active_waiver", + "admission_policy", + "booking_rules", + "categories", + "chat_templates", + "email_mutes", + "email_templates", + "features", + "item_templates", + "loan_policy", + "membership_plans", + "modules", + "notification_defaults", + "opening_hours", + "settings" ] }, - "NotificationRuleChange": { + "PackEmailNotificationMute": { "type": "object", "properties": { "target": { - "type": "string" + "type": "string", + "maxLength": 32 }, "stream": { - "type": "string" + "type": "string", + "maxLength": 16 }, "event": { - "type": "string" + "type": "string", + "maxLength": 64 }, "audience": { - "type": "string" - }, - "muted": { - "type": "boolean" + "type": "string", + "maxLength": 16 } }, "required": [ "audience", "event", - "muted", "stream", "target" ] }, - "NotificationRuleMute": { + "PackEmailTemplate": { "type": "object", "properties": { - "target": { - "type": "string" - }, "stream": { - "type": "string" + "$ref": "#/components/schemas/StreamEnum" }, - "event": { + "audience": { + "$ref": "#/components/schemas/PackEmailTemplateAudienceEnum" + }, + "key": { + "type": "string", + "maxLength": 32 + }, + "subject": { + "type": "string", + "maxLength": 200 + }, + "text_body": { "type": "string" }, - "audience": { + "html_body": { "type": "string" + }, + "is_active": { + "type": "boolean" } }, "required": [ "audience", - "event", + "html_body", + "is_active", + "key", "stream", - "target" + "subject", + "text_body" ] }, - "NotificationRulesResponse": { + "PackEmailTemplateAudienceEnum": { + "enum": [ + "requester", + "staff" + ], + "type": "string", + "description": "* `requester` - Requester\n* `staff` - Staff" + }, + "PackError": { "type": "object", "properties": { - "catalog": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationRuleCatalogItem" - } + "code": { + "type": "string" }, - "mutes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationRuleMute" - } + "detail": { + "type": "string" }, - "channels": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationChannel" - } + "fields": {}, + "feature": { + "type": "string" }, - "features": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationFeature" - } + "module": { + "type": "string" }, - "preferences": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationPreferenceCell" - } + "purpose": { + "type": "string" } - }, - "required": [ - "catalog", - "channels", - "features", - "mutes", - "preferences" - ] + } }, - "NotificationUnreadCount": { + "PackImport": { "type": "object", "properties": { - "count": { - "type": "integer" + "pack": { + "$ref": "#/components/schemas/SpacePack" + }, + "dry_run": { + "type": "boolean", + "default": true + }, + "base_checksum": { + "type": "string", + "maxLength": 64, + "minLength": 64 } }, "required": [ - "count" - ] - }, - "NullEnum": { - "enum": [ - null + "pack" ] }, - "OccurrenceResult": { + "PackInventoryProduct": { "type": "object", "properties": { - "occurrence": { - "type": "integer" - }, - "starts_at": { + "name": { "type": "string", - "format": "date-time" + "maxLength": 200 }, - "ends_at": { + "category": { "type": "string", - "format": "date-time" + "nullable": true }, - "booking_id": { + "description": { + "type": "string" + }, + "tracking_mode": { + "$ref": "#/components/schemas/TrackingModeB86Enum" + }, + "loan_days": { "type": "integer", + "maximum": 365, + "minimum": 1, "nullable": true }, - "status": { - "type": "string" + "max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "nullable": true }, - "detail": { - "type": "string" + "is_public": { + "type": "boolean" + }, + "public_self_checkout_enabled": { + "type": "boolean" + }, + "show_public_count": { + "type": "boolean" + }, + "public_availability_mode": { + "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" } }, "required": [ - "booking_id", - "detail", - "ends_at", - "occurrence", - "starts_at", - "status" + "category", + "description", + "is_public", + "loan_days", + "max_renewals", + "name", + "public_availability_mode", + "public_self_checkout_enabled", + "show_public_count", + "tracking_mode" ] }, - "OfflineCheckInOperation": { + "PackMakerspaceLoanPolicy": { "type": "object", "properties": { - "operation_id": { - "type": "string", - "format": "uuid" + "default_loan_days": { + "type": "integer", + "maximum": 365, + "minimum": 1 }, - "checkin_token": { - "type": "string", - "maxLength": 64 + "loan_renewal_mode": { + "$ref": "#/components/schemas/LoanRenewalModeEnum" }, - "reported_occurred_at": { - "type": "string", - "format": "date-time" + "loan_max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0 + }, + "loan_renewal_days": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "loan_max_active": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "loan_max_items": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "loan_reminder_offsets_hours": {}, + "loan_escalate_overdue_days": { + "type": "integer", + "maximum": 365, + "minimum": 0 } }, "required": [ - "checkin_token", - "operation_id", - "reported_occurred_at" + "default_loan_days", + "loan_escalate_overdue_days", + "loan_max_active", + "loan_max_items", + "loan_max_renewals", + "loan_reminder_offsets_hours", + "loan_renewal_days", + "loan_renewal_mode" ] }, - "OfflineCheckInResult": { + "PackMakerspaceMembershipSettings": { "type": "object", "properties": { - "operation_id": { - "type": "string", - "format": "uuid" + "join_mode": { + "$ref": "#/components/schemas/JoinModeEnum" }, - "outcome": { - "$ref": "#/components/schemas/OfflineCheckInResultOutcomeEnum" + "vouches_required": { + "type": "integer", + "maximum": 32767, + "minimum": 0 }, - "registration_id": { - "type": "integer" + "voucher_min_tenure_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "attended_at": { + "voucher_pending_limit": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "vouch_expiry_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "vouch_replaces_approval": { + "type": "boolean" + }, + "payment_timing": { + "$ref": "#/components/schemas/PaymentTimingEnum" + }, + "application_schema": { + "nullable": true + }, + "default_plan": { "type": "string", - "format": "date-time" + "nullable": true + }, + "joining_fee": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "joining_fee_currency": { + "type": "string", + "maxLength": 3 + }, + "rejoin_dues_rule": { + "$ref": "#/components/schemas/RejoinDuesRuleEnum" + }, + "request_expiry_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 } }, "required": [ - "operation_id", - "outcome" + "application_schema", + "default_plan", + "join_mode", + "joining_fee", + "joining_fee_currency", + "payment_timing", + "rejoin_dues_rule", + "request_expiry_days", + "vouch_expiry_days", + "vouch_replaces_approval", + "voucher_min_tenure_days", + "voucher_pending_limit", + "vouches_required" ] }, - "OfflineCheckInResultOutcomeEnum": { - "enum": [ - "applied", - "duplicate_operation", - "already_attended", - "registration_changed", - "event_unavailable", - "invalid_token", - "outside_window" - ], - "type": "string", - "description": "* `applied` - applied\n* `duplicate_operation` - duplicate_operation\n* `already_attended` - already_attended\n* `registration_changed` - registration_changed\n* `event_unavailable` - event_unavailable\n* `invalid_token` - invalid_token\n* `outside_window` - outside_window" - }, - "OfflineCheckInSyncRequest": { + "PackMakerspaceWaiver": { "type": "object", "properties": { - "lease_token": { + "version": { "type": "string", - "maxLength": 8192 + "maxLength": 64 }, - "operations": { - "type": "array", - "items": { - "$ref": "#/components/schemas/OfflineCheckInOperation" - } + "body": { + "type": "string" } }, "required": [ - "lease_token", - "operations" + "body", + "version" ] }, - "OfflineCheckInSyncResponse": { + "PackMembershipPlan": { "type": "object", "properties": { - "recorded_at": { + "name": { "type": "string", - "format": "date-time" + "maxLength": 120 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/OfflineCheckInResult" - } + "interval": { + "$ref": "#/components/schemas/IntervalEnum" + }, + "custom_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "currency": { + "type": "string", + "maxLength": 3 + }, + "is_active": { + "type": "boolean" + }, + "loan_max_active": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + }, + "loan_max_items": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true } }, "required": [ - "recorded_at", - "results" + "amount", + "currency", + "custom_days", + "interval", + "is_active", + "loan_max_active", + "loan_max_items", + "name" ] }, - "OfflineRosterEvent": { + "PackNotificationPreference": { "type": "object", "properties": { - "id": { - "type": "integer" - }, - "title": { - "type": "string" + "feature": { + "$ref": "#/components/schemas/Feature4feEnum" }, - "starts_at": { - "type": "string", - "format": "date-time" + "channel": { + "$ref": "#/components/schemas/Channel292Enum" }, - "ends_at": { - "type": "string", - "format": "date-time" + "enabled": { + "type": "boolean" } }, "required": [ - "ends_at", - "id", - "starts_at", - "title" + "channel", + "enabled", + "feature" ] }, - "OfflineRosterRegistration": { + "PackOpeningHours": { "type": "object", "properties": { - "registration_id": { - "type": "integer" - }, - "checkin_token": { - "type": "string", - "format": "uuid" - }, - "name": { + "timezone": { "type": "string" }, - "host_waiver_state": { - "$ref": "#/components/schemas/HostWaiverStateEnum" + "opening_hours": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "required": [ + "weekly", + "exceptions" + ], + "additionalProperties": false, + "properties": { + "weekly": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + "sun" + ], + "properties": { + "mon": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "tue": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "wed": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "thu": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "fri": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sat": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sun": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + } + } + } + ] + }, + "exceptions": { + "type": "object", + "maxProperties": 366, + "additionalProperties": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." + } + } + } + ], + "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." } }, "required": [ - "checkin_token", - "host_waiver_state", - "name", - "registration_id" + "opening_hours", + "timezone" ] }, - "OfflineRosterResponse": { + "PackPreview": { "type": "object", "properties": { - "lease_token": { + "base_checksum": { "type": "string" }, - "lease_id": { - "type": "string", - "format": "uuid" - }, - "server_time": { - "type": "string", - "format": "date-time" - }, - "issued_at": { - "type": "string", - "format": "date-time" - }, - "expires_at": { - "type": "string", - "format": "date-time" - }, - "scan_opens_at": { - "type": "string", - "format": "date-time" - }, - "scan_closes_at": { - "type": "string", - "format": "date-time" - }, - "sync_deadline": { - "type": "string", - "format": "date-time" - }, - "event": { - "$ref": "#/components/schemas/OfflineRosterEvent" + "applied": { + "type": "boolean" }, - "registrations": { + "changes": { "type": "array", "items": { - "$ref": "#/components/schemas/OfflineRosterRegistration" + "$ref": "#/components/schemas/PackChange" } } }, "required": [ - "event", - "expires_at", - "issued_at", - "lease_id", - "lease_token", - "registrations", - "scan_closes_at", - "scan_opens_at", - "server_time", - "sync_deadline" + "applied", + "base_checksum", + "changes" ] }, - "OidcBrowserCallback": { + "PackProfile": { "type": "object", "properties": { - "code": { + "id": { + "type": "integer", + "readOnly": true + }, + "name": { "type": "string", - "maxLength": 4096 + "maxLength": 120 }, - "state": { + "pack": { + "$ref": "#/components/schemas/SpacePack" + }, + "created_at": { "type": "string", - "maxLength": 512 + "format": "date-time", + "readOnly": true }, - "nonce": { + "updated_at": { "type": "string", - "maxLength": 512 + "format": "date-time", + "readOnly": true } }, "required": [ - "code", - "nonce", - "state" + "created_at", + "id", + "name", + "pack", + "updated_at" ] }, - "OidcBrowserLoginResponse": { + "PackSettings": { "type": "object", + "description": "Schema only: actual writes validate through MakerspaceSerializer.", "properties": { - "access": { - "type": "string" + "public_inventory_enabled": { + "type": "boolean" }, - "user": { - "type": "object", - "additionalProperties": {} + "public_site_enabled": { + "type": "boolean" }, - "outcome": { - "type": "string" - } - }, - "required": [ - "access", - "outcome", - "user" - ] - }, - "OidcBrowserStart": { - "type": "object", - "properties": { - "redirect_uri": { - "type": "string", - "format": "uri", - "maxLength": 2048 + "public_landing_page": { + "$ref": "#/components/schemas/PublicLandingPage6a0Enum" }, - "email": { - "oneOf": [ - { - "type": "string", - "format": "email" - }, - { - "type": "string", - "maxLength": 0 - } - ] + "public_stats_enabled": { + "type": "boolean" }, - "makerspace_slug": { - "oneOf": [ - { - "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" - }, - { - "type": "string", - "maxLength": 0 - } - ] - } - }, - "required": [ - "redirect_uri" - ] - }, - "OidcBrowserStartResponse": { - "type": "object", - "properties": { - "authorization_url": { + "public_stats_show_holder_names": { + "type": "boolean" + }, + "public_print_status_lookup_policy": { + "$ref": "#/components/schemas/PublicPrintStatusLookupPolicyEnum" + }, + "membership_policy": { + "$ref": "#/components/schemas/MembershipPolicyEnum" + }, + "membership_dues_amount": { "type": "string", - "format": "uri" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "state": { - "type": "string" + "referrals_enabled": { + "type": "boolean" }, - "nonce": { - "type": "string" + "lapsed_members_cannot_borrow": { + "type": "boolean" }, - "expires_in": { - "type": "integer" - } + "staff_notifications_enabled": { + "type": "boolean" + }, + "booking_requester_notifications_enabled": { + "type": "boolean" + }, + "default_loan_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "presence_preset_minutes": {} }, "required": [ - "authorization_url", - "expires_in", - "nonce", - "state" + "booking_requester_notifications_enabled", + "default_loan_days", + "lapsed_members_cannot_borrow", + "membership_dues_amount", + "membership_policy", + "presence_preset_minutes", + "public_inventory_enabled", + "public_landing_page", + "public_print_status_lookup_policy", + "public_site_enabled", + "public_stats_enabled", + "public_stats_show_holder_names", + "referrals_enabled", + "staff_notifications_enabled" ] }, - "OperatorCandidate": { + "PaginatedAdminRequestList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "user_id": { + "count": { "type": "integer", - "readOnly": true + "example": 123 }, - "username": { + "next": { "type": "string", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "display_name": { + "previous": { "type": "string", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AdminRequest" + } } - }, - "required": [ - "display_name", - "user_id", - "username" - ] - }, - "OperatorEnum": { - "enum": [ - "gt", - "gte", - "lt", - "lte", - "eq" - ], - "type": "string", - "description": "* `gt` - gt\n* `gte` - gte\n* `lt` - lt\n* `lte` - lte\n* `eq` - eq" + } }, - "OrganizationCreate": { + "PaginatedApiClientList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "name": { - "type": "string", - "maxLength": 200 + "count": { + "type": "integer", + "example": 123 }, - "slug": { + "next": { "type": "string", - "maxLength": 50, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "description": { - "type": "string" + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "website": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "public_profile_enabled": { - "type": "boolean" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApiClient" + } } - }, - "required": [ - "name", - "slug" - ] + } }, - "OrganizationDetail": { + "PaginatedApiKeyRequestList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "id": { + "count": { "type": "integer", - "readOnly": true + "example": 123 }, - "slug": { + "next": { "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "name": { + "previous": { "type": "string", - "readOnly": true - }, - "governance_actions": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "granted_actions": { + "results": { "type": "array", "items": { - "type": "string" - }, - "readOnly": true - }, - "description": { - "type": "string", - "readOnly": true + "$ref": "#/components/schemas/ApiKeyRequest" + } + } + } + }, + "PaginatedAuditLogList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { + "type": "integer", + "example": 123 }, - "website": { + "next": { "type": "string", + "nullable": true, "format": "uri", - "readOnly": true + "example": "http://api.example.org/accounts/?page=4" }, - "logo_url": { + "previous": { "type": "string", - "format": "uri", "nullable": true, - "readOnly": true - }, - "public_profile_enabled": { - "type": "boolean", - "readOnly": true - }, - "is_active": { - "type": "boolean", - "readOnly": true - }, - "legal_name": { - "type": "string", - "readOnly": true - }, - "registration_number": { - "type": "string", - "readOnly": true - }, - "contact_email": { - "type": "string", - "format": "email", - "readOnly": true + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "billing_email": { - "type": "string", - "format": "email", - "readOnly": true + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/AuditLog" + } } - }, - "required": [ - "billing_email", - "contact_email", - "description", - "governance_actions", - "granted_actions", - "id", - "is_active", - "legal_name", - "logo_url", - "name", - "public_profile_enabled", - "registration_number", - "slug", - "website" - ] + } }, - "OrganizationEventHost": { + "PaginatedBindingList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "slug": { - "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "count": { + "type": "integer", + "example": 123 }, - "name": { + "next": { "type": "string", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "logo_url": { + "previous": { "type": "string", - "format": "uri", "nullable": true, - "readOnly": true + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Binding" + } } - }, - "required": [ - "logo_url", - "name", - "slug" - ] + } }, - "OrganizationInvitation": { + "PaginatedBoxList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "organization_id": { - "type": "integer", - "readOnly": true - }, - "governance_actions": { - "readOnly": true - }, - "granted_actions": { - "readOnly": true - }, - "expires_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "redeemed_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "revoked_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "created_by_id": { + "count": { "type": "integer", - "readOnly": true, - "nullable": true + "example": 123 }, - "redeemed_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "created_at": { + "previous": { "type": "string", - "format": "date-time", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "state": { - "allOf": [ - { - "$ref": "#/components/schemas/State31eEnum" - } - ], - "readOnly": true + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Box" + } } - }, - "required": [ - "created_at", - "created_by_id", - "expires_at", - "governance_actions", - "granted_actions", - "id", - "organization_id", - "redeemed_at", - "redeemed_by_id", - "revoked_at", - "state" - ] + } }, - "OrganizationInvitationCreate": { + "PaginatedCategoryAdminList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "governance_actions": { - "type": "array", - "items": { - "type": "string" - } + "count": { + "type": "integer", + "example": 123 }, - "granted_actions": { + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/CategoryAdmin" } - }, - "expires_in_days": { - "type": "integer", - "maximum": 30, - "minimum": 1, - "default": 7 } } }, - "OrganizationInvitationCreated": { + "PaginatedDeviceList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "organization_id": { + "count": { "type": "integer", - "readOnly": true - }, - "governance_actions": { - "readOnly": true - }, - "granted_actions": { - "readOnly": true - }, - "expires_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "example": 123 }, - "redeemed_at": { + "next": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "revoked_at": { + "previous": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "created_by_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "redeemed_by_id": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Device" + } + } + } + }, + "PaginatedDirectLoanList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { "type": "integer", - "readOnly": true, - "nullable": true + "example": 123 }, - "created_at": { + "next": { "type": "string", - "format": "date-time", - "readOnly": true - }, - "state": { - "allOf": [ - { - "$ref": "#/components/schemas/State31eEnum" - } - ], - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "token": { + "previous": { "type": "string", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "redeem_path": { - "type": "string", - "readOnly": true + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DirectLoan" + } } - }, - "required": [ - "created_at", - "created_by_id", - "expires_at", - "governance_actions", - "granted_actions", - "id", - "organization_id", - "redeem_path", - "redeemed_at", - "redeemed_by_id", - "revoked_at", - "state", - "token" - ] + } }, - "OrganizationInvitationList": { + "PaginatedDirectLoanMemberList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { "count": { - "type": "integer" + "type": "integer", + "example": 123 }, "next": { "type": "string", - "nullable": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", - "nullable": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/OrganizationInvitation" + "$ref": "#/components/schemas/DirectLoanMember" } } - }, + } + }, + "PaginatedEmailLogList": { + "type": "object", "required": [ "count", - "next", - "previous", "results" - ] - }, - "OrganizationInvitationRedeem": { - "type": "object", + ], "properties": { - "token": { + "count": { + "type": "integer", + "example": 123 + }, + "next": { "type": "string", - "maxLength": 200, - "minLength": 20 + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EmailLog" + } } - }, - "required": [ - "token" - ] + } }, - "OrganizationInvitationRedeemed": { + "PaginatedInventoryAssetAdminList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "user": { - "$ref": "#/components/schemas/AuthUserPayload" + "count": { + "type": "integer", + "example": 123 }, - "membership": { - "$ref": "#/components/schemas/OrganizationMembership" + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InventoryAssetAdmin" + } } - }, - "required": [ - "membership", - "user" - ] + } }, - "OrganizationLink": { + "PaginatedInventoryProductAdminList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "id": { + "count": { "type": "integer", - "readOnly": true + "example": 123 }, - "makerspace_id": { - "type": "integer", - "readOnly": true + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "makerspace_name": { + "previous": { "type": "string", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "relationship": { - "$ref": "#/components/schemas/RelationshipEnum" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InventoryProductAdmin" + } } - }, - "required": [ - "id", - "makerspace_id", - "makerspace_name", - "relationship" - ] + } }, - "OrganizationLinkPage": { + "PaginatedJobList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { "count": { - "type": "integer" + "type": "integer", + "example": 123 }, "next": { "type": "string", + "nullable": true, "format": "uri", - "nullable": true + "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", + "nullable": true, "format": "uri", - "nullable": true + "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/OrganizationLink" + "$ref": "#/components/schemas/Job" } } - }, + } + }, + "PaginatedLabelCommandList": { + "type": "object", "required": [ "count", - "next", - "previous", "results" - ] - }, - "OrganizationLinkWrite": { - "type": "object", + ], "properties": { - "makerspace_id": { + "count": { "type": "integer", - "minimum": 1 + "example": 123 }, - "relationship": { - "$ref": "#/components/schemas/RelationshipEnum" + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LabelCommand" + } } - }, - "required": [ - "makerspace_id", - "relationship" - ] + } }, - "OrganizationList": { + "PaginatedMaterialList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { "count": { - "type": "integer" + "type": "integer", + "example": 123 }, "next": { "type": "string", - "nullable": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", - "nullable": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/OrganizationSummary" + "$ref": "#/components/schemas/Material" } } - }, + } + }, + "PaginatedNotificationList": { + "type": "object", "required": [ "count", - "next", - "previous", "results" - ] - }, - "OrganizationMembership": { - "type": "object", + ], "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "user_id": { + "count": { "type": "integer", - "readOnly": true - }, - "username": { - "type": "string", - "readOnly": true + "example": 123 }, - "display_name": { + "next": { "type": "string", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "email": { + "previous": { "type": "string", - "format": "email", - "readOnly": true - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/OrganizationMembershipStatusEnum" - } - ], - "readOnly": true - }, - "governance_actions": { - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "granted_actions": { - "readOnly": true + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Notification" + } + } + } + }, + "PaginatedOverdueScheduleList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { + "type": "integer", + "example": 123 }, - "created_at": { + "next": { "type": "string", - "format": "date-time", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "updated_at": { + "previous": { "type": "string", - "format": "date-time", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OverdueSchedule" + } } - }, - "required": [ - "created_at", - "display_name", - "email", - "governance_actions", - "granted_actions", - "id", - "status", - "updated_at", - "user_id", - "username" - ] + } }, - "OrganizationMembershipList": { + "PaginatedPackProfileList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { "count": { - "type": "integer" + "type": "integer", + "example": 123 }, "next": { "type": "string", - "nullable": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, "previous": { "type": "string", - "nullable": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/OrganizationMembership" + "$ref": "#/components/schemas/PackProfile" } } - }, + } + }, + "PaginatedPublicMachineDetailList": { + "type": "object", "required": [ "count", - "next", - "previous", "results" - ] - }, - "OrganizationMembershipStatusEnum": { - "enum": [ - "active", - "suspended" ], - "type": "string", - "description": "* `active` - Active\n* `suspended` - Suspended" - }, - "OrganizationReportBreakdown": { - "type": "object", "properties": { - "rows": { + "count": { + "type": "integer", + "example": 123 + }, + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { "type": "array", "items": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/PublicMachineDetail" } - }, - "makerspace_id": { - "type": "integer" } - }, - "required": [ - "makerspace_id", - "rows" - ] + } }, - "OrganizationReportResponse": { + "PaginatedPublicMachineList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "report_key": { - "type": "string" + "count": { + "type": "integer", + "example": 123 }, - "strategy": { - "$ref": "#/components/schemas/StrategyEnum" + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "breakdown": { + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/OrganizationReportBreakdown" + "$ref": "#/components/schemas/PublicMachine" } - }, - "total": { - "$ref": "#/components/schemas/OrganizationReportRows" } - }, - "required": [ - "breakdown", - "report_key", - "strategy", - "total" - ] + } }, - "OrganizationReportRows": { + "PaginatedPublicProductList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "rows": { + "count": { + "type": "integer", + "example": 123 + }, + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" + }, + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { "type": "array", "items": { - "type": "object", - "additionalProperties": {} + "$ref": "#/components/schemas/PublicProduct" } } - }, - "required": [ - "rows" - ] + } }, - "OrganizationSummary": { + "PaginatedQrPrintBatchList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "id": { + "count": { "type": "integer", - "readOnly": true + "example": 123 }, - "slug": { + "next": { "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "name": { + "previous": { "type": "string", - "readOnly": true - }, - "governance_actions": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "granted_actions": { + "results": { "type": "array", "items": { - "type": "string" - }, - "readOnly": true + "$ref": "#/components/schemas/QrPrintBatch" + } } - }, - "required": [ - "governance_actions", - "granted_actions", - "id", - "name", - "slug" - ] - }, - "OrientationEnum": { - "enum": [ - "portrait", - "landscape" - ], - "type": "string", - "description": "* `portrait` - portrait\n* `landscape` - landscape" + } }, - "OtpResetPasswordConfirm": { + "PaginatedReorderSuggestionList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "email": { - "type": "string", - "format": "email" + "count": { + "type": "integer", + "example": 123 }, - "code": { + "next": { "type": "string", - "writeOnly": true, - "pattern": "^\\d{6}$" + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "new_password": { + "previous": { "type": "string", - "writeOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReorderSuggestion" + } } - }, - "required": [ - "code", - "email", - "new_password" - ] - }, - "OutcomeA1aEnum": { - "enum": [ - "succeeded", - "retryable_failed", - "failed" - ], - "type": "string", - "description": "* `succeeded` - succeeded\n* `retryable_failed` - retryable_failed\n* `failed` - failed" + } }, - "OverdueSchedule": { + "PaginatedReplayRecoveryList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "machine_id": { + "count": { "type": "integer", - "readOnly": true + "example": 123 }, - "machine_name": { + "next": { "type": "string", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "description": { + "previous": { "type": "string", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "next_due": { - "type": "string", - "format": "date", - "readOnly": true + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReplayRecovery" + } } - }, - "required": [ - "description", - "id", - "machine_id", - "machine_name", - "next_due" - ] + } }, - "OwnMemberCard": { + "PaginatedReportScheduleList": { "type": "object", - "description": "Never carries the photo key or a URL that outlives the response.", + "required": [ + "count", + "results" + ], "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "card_number": { + "count": { "type": "integer", - "readOnly": true + "example": 123 }, - "printed_name": { + "next": { "type": "string", - "readOnly": true - }, - "membership_id": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "is_active": { - "type": "boolean", - "readOnly": true - }, - "photo_set": { - "type": "boolean", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "photo_consent_at": { + "previous": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "qr_active": { - "type": "boolean", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "print_count": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReportSchedule" + } + } + } + }, + "PaginatedScheduleList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { "type": "integer", - "readOnly": true - }, - "last_printed_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "issued_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "revoked_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "revoked_reason": { - "type": "string", - "readOnly": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "example": 123 }, - "updated_at": { + "next": { "type": "string", - "format": "date-time", - "readOnly": true + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "qr_image": { + "previous": { "type": "string", "nullable": true, - "readOnly": true + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Schedule" + } } - }, - "required": [ - "card_number", - "created_at", - "id", - "is_active", - "issued_at", - "last_printed_at", - "membership_id", - "photo_consent_at", - "photo_set", - "print_count", - "printed_name", - "qr_active", - "qr_image", - "revoked_at", - "revoked_reason", - "updated_at" - ] + } }, - "PackBookingRules": { + "PaginatedStaffInvitationList": { "type": "object", - "description": "Query-free shape: bookings may be tombstoned on this deployment.", + "required": [ + "count", + "results" + ], "properties": { - "name": { - "type": "string", - "maxLength": 200 - }, - "min_booking_duration_minutes": { - "type": "integer", - "minimum": 1 - }, - "max_booking_duration_minutes": { + "count": { "type": "integer", - "minimum": 1 + "example": 123 }, - "booking_lead_time_minutes": { - "type": "integer", - "minimum": 0 + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "max_booking_advance_days": { - "type": "integer", - "minimum": 1 + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "approval_mode": { - "$ref": "#/components/schemas/PackBookingRulesApprovalModeEnum" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaffInvitation" + } } - }, - "required": [ - "approval_mode", - "booking_lead_time_minutes", - "max_booking_advance_days", - "max_booking_duration_minutes", - "min_booking_duration_minutes", - "name" - ] - }, - "PackBookingRulesApprovalModeEnum": { - "enum": [ - "instant", - "approve" - ], - "type": "string", - "description": "* `instant` - instant\n* `approve` - approve" + } }, - "PackCategory": { + "PaginatedStaffMembershipList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "slug": { - "type": "string", - "maxLength": 120, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "name": { - "type": "string", - "maxLength": 100 - }, - "display_order": { + "count": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "example": 123 }, - "icon": { + "next": { "type": "string", - "maxLength": 50 + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1, - "nullable": true + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0, - "nullable": true + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaffMembership" + } } - }, - "required": [ - "display_order", - "icon", - "loan_days", - "max_renewals", - "name", - "slug" - ] + } }, - "PackChange": { + "PaginatedStockTransferList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "section": { - "type": "string" + "count": { + "type": "integer", + "example": 123 }, - "key": { - "type": "string" + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "before": { - "nullable": true + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "after": { - "nullable": true + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StockTransfer" + } } - }, - "required": [ - "after", - "before", - "key", - "section" - ] + } }, - "PackChatTemplate": { + "PaginatedStocktakeList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "feature": { - "$ref": "#/components/schemas/Feature4feEnum" + "count": { + "type": "integer", + "example": 123 }, - "event": { + "next": { "type": "string", - "maxLength": 64 + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "text_body": { - "type": "string" + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "is_active": { - "type": "boolean" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Stocktake" + } } - }, - "required": [ - "event", - "feature", - "is_active", - "text_body" - ] + } }, - "PackConfiguration": { + "PaginatedSubdomainRequestList": { "type": "object", + "required": [ + "count", + "results" + ], "properties": { - "modules": { - "type": "array", - "items": { - "type": "string" - } - }, - "features": { - "type": "array", - "items": { - "type": "string" - } + "count": { + "type": "integer", + "example": 123 }, - "settings": { - "$ref": "#/components/schemas/PackSettings" + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "opening_hours": { - "$ref": "#/components/schemas/PackOpeningHours" + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "roles": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/PackMakerspaceRole" + "$ref": "#/components/schemas/SubdomainRequest" } + } + } + }, + "PaginatedWarrantyReportRowList": { + "type": "object", + "required": [ + "count", + "results" + ], + "properties": { + "count": { + "type": "integer", + "example": 123 }, - "active_waiver": { - "allOf": [ - { - "$ref": "#/components/schemas/PackMakerspaceWaiver" - } - ], - "nullable": true + "next": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=4" }, - "loan_policy": { - "allOf": [ - { - "$ref": "#/components/schemas/PackMakerspaceLoanPolicy" - } - ], - "nullable": true + "previous": { + "type": "string", + "nullable": true, + "format": "uri", + "example": "http://api.example.org/accounts/?page=2" }, - "membership_plans": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/PackMembershipPlan" + "$ref": "#/components/schemas/WarrantyReportRow" } + } + } + }, + "Pairing": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid", + "readOnly": true }, - "admission_policy": { - "allOf": [ - { - "$ref": "#/components/schemas/PackMakerspaceMembershipSettings" - } - ], - "nullable": true + "migration_id": { + "type": "string", + "format": "uuid" }, - "booking_rules": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackBookingRules" - } + "source_tenant_id": { + "type": "string", + "maxLength": 64 }, - "notification_defaults": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackNotificationPreference" - } + "archive_digest": { + "type": "string", + "maxLength": 64 }, - "email_mutes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackEmailNotificationMute" - } + "source_deployment_id": { + "type": "string", + "maxLength": 128 }, - "email_templates": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackEmailTemplate" - } + "source_fingerprint": { + "type": "string", + "maxLength": 64 }, - "chat_templates": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackChatTemplate" - } + "target_deployment_id": { + "type": "string", + "maxLength": 128 }, - "categories": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackCategory" - } + "target_fingerprint": { + "type": "string", + "maxLength": 64 }, - "item_templates": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackInventoryProduct" - } + "approved_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ - "active_waiver", - "admission_policy", - "booking_rules", - "categories", - "chat_templates", - "email_mutes", - "email_templates", - "features", - "item_templates", - "loan_policy", - "membership_plans", - "modules", - "notification_defaults", - "opening_hours", - "roles", - "settings" + "approved_at", + "archive_digest", + "id", + "migration_id", + "source_deployment_id", + "source_fingerprint", + "source_tenant_id", + "target_deployment_id", + "target_fingerprint" ] }, - "PackEmailNotificationMute": { + "PairingCreate": { "type": "object", "properties": { - "target": { - "type": "string", - "maxLength": 32 - }, - "stream": { + "migration_id": { "type": "string", - "maxLength": 16 + "format": "uuid" }, - "event": { + "source_tenant_id": { "type": "string", "maxLength": 64 }, - "audience": { + "archive_digest": { "type": "string", - "maxLength": 16 + "pattern": "^[0-9a-f]{64}$" + }, + "source": { + "type": "object", + "additionalProperties": {} + }, + "target": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "audience", - "event", - "stream", + "archive_digest", + "migration_id", + "source", + "source_tenant_id", "target" ] }, - "PackEmailTemplate": { + "PaperSizeEnum": { + "enum": [ + "A4", + "LETTER", + "custom" + ], + "type": "string", + "description": "* `A4` - A4\n* `LETTER` - LETTER\n* `custom` - custom" + }, + "ParticipatingOrganization": { "type": "object", "properties": { - "stream": { - "$ref": "#/components/schemas/StreamEnum" - }, - "audience": { - "$ref": "#/components/schemas/PackEmailTemplateAudienceEnum" - }, - "key": { - "type": "string", - "maxLength": 32 + "id": { + "type": "integer", + "readOnly": true }, - "subject": { + "name": { "type": "string", - "maxLength": 200 - }, - "text_body": { - "type": "string" - }, - "html_body": { - "type": "string" - }, - "is_active": { - "type": "boolean" + "readOnly": true } }, "required": [ - "audience", - "html_body", - "is_active", - "key", - "stream", - "subject", - "text_body" + "id", + "name" ] }, - "PackEmailTemplateAudienceEnum": { + "PartitionEnum": { "enum": [ - "requester", - "staff" + "tenant", + "currency", + "unit" ], "type": "string", - "description": "* `requester` - Requester\n* `staff` - Staff" + "description": "* `tenant` - tenant\n* `currency` - currency\n* `unit` - unit" }, - "PackError": { + "PasswordLoginResult": { + "oneOf": [ + { + "$ref": "#/components/schemas/LoginResponse" + }, + { + "$ref": "#/components/schemas/MFAChallengeResponse" + } + ] + }, + "PasswordResetAcknowledgement": { "type": "object", "properties": { - "code": { - "type": "string" - }, "detail": { "type": "string" - }, - "fields": {}, - "feature": { - "type": "string" - }, - "module": { - "type": "string" - }, - "purpose": { - "type": "string" } - } + }, + "required": [ + "detail" + ] }, - "PackImport": { + "PasswordResetFailure": { "type": "object", "properties": { - "pack": { - "$ref": "#/components/schemas/SpacePack" - }, - "dry_run": { - "type": "boolean", - "default": true - }, - "base_checksum": { - "type": "string", - "maxLength": 64, - "minLength": 64 + "detail": { + "type": "string" } }, "required": [ - "pack" + "detail" ] }, - "PackInventoryProduct": { + "PasswordUpdated": { "type": "object", "properties": { - "name": { - "type": "string", - "maxLength": 200 - }, - "category": { - "type": "string", - "nullable": true - }, - "description": { + "detail": { "type": "string" - }, - "tracking_mode": { - "$ref": "#/components/schemas/TrackingModeB86Enum" - }, - "loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1, - "nullable": true - }, - "max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0, - "nullable": true - }, - "is_public": { - "type": "boolean" - }, - "public_self_checkout_enabled": { - "type": "boolean" - }, - "show_public_count": { - "type": "boolean" - }, - "public_availability_mode": { - "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" } }, "required": [ - "category", - "description", - "is_public", - "loan_days", - "max_renewals", - "name", - "public_availability_mode", - "public_self_checkout_enabled", - "show_public_count", - "tracking_mode" + "detail" ] }, - "PackMakerspaceLoanPolicy": { + "PasswordValidationFailure": { "type": "object", "properties": { - "default_loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1 - }, - "loan_renewal_mode": { - "$ref": "#/components/schemas/LoanRenewalModeEnum" - }, - "loan_max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0 - }, - "loan_renewal_days": { - "type": "integer", - "maximum": 365, - "minimum": 1 - }, - "loan_max_active": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "loan_max_items": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "loan_reminder_offsets_hours": {}, - "loan_escalate_overdue_days": { - "type": "integer", - "maximum": 365, - "minimum": 0 + "new_password": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "default_loan_days", - "loan_escalate_overdue_days", - "loan_max_active", - "loan_max_items", - "loan_max_renewals", - "loan_reminder_offsets_hours", - "loan_renewal_days", - "loan_renewal_mode" + "new_password" ] }, - "PackMakerspaceMembershipSettings": { + "PatchedAccountProfile": { + "type": "object", + "properties": { + "display_name": { + "type": "string", + "maxLength": 200 + }, + "first_name": { + "type": "string", + "maxLength": 150 + }, + "last_name": { + "type": "string", + "maxLength": 150 + }, + "phone": { + "type": "string", + "maxLength": 32 + } + } + }, + "PatchedAdmissionSettings": { "type": "object", "properties": { + "configured": { + "type": "boolean", + "readOnly": true + }, "join_mode": { "$ref": "#/components/schemas/JoinModeEnum" }, "vouches_required": { "type": "integer", - "maximum": 32767, - "minimum": 0 + "maximum": 5, + "minimum": 1 }, "voucher_min_tenure_days": { "type": "integer", - "maximum": 2147483647, + "maximum": 3650, "minimum": 0 }, "voucher_pending_limit": { "type": "integer", - "maximum": 32767, - "minimum": 0 + "maximum": 100, + "minimum": 1 }, "vouch_expiry_days": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "maximum": 365, + "minimum": 1 }, "vouch_replaces_approval": { "type": "boolean" @@ -88926,7 +111317,7 @@ "nullable": true }, "default_plan": { - "type": "string", + "type": "integer", "nullable": true }, "joining_fee": { @@ -88936,393 +111327,221 @@ }, "joining_fee_currency": { "type": "string", - "maxLength": 3 + "pattern": "^[A-Za-z]{3}$" }, "rejoin_dues_rule": { "$ref": "#/components/schemas/RejoinDuesRuleEnum" }, "request_expiry_days": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "maximum": 365, + "minimum": 1 + }, + "recipe": { + "allOf": [ + { + "$ref": "#/components/schemas/RecipeEnum" + } + ], + "writeOnly": true } - }, - "required": [ - "application_schema", - "default_plan", - "join_mode", - "joining_fee", - "joining_fee_currency", - "payment_timing", - "rejoin_dues_rule", - "request_expiry_days", - "vouch_expiry_days", - "vouch_replaces_approval", - "voucher_min_tenure_days", - "voucher_pending_limit", - "vouches_required" - ] + } }, - "PackMakerspaceRole": { + "PatchedApiClient": { "type": "object", "properties": { - "name": { - "type": "string", - "maxLength": 80 + "id": { + "type": "integer", + "readOnly": true }, - "granted_actions": {} - }, - "required": [ - "granted_actions", - "name" - ] - }, - "PackMakerspaceWaiver": { - "type": "object", - "properties": { - "version": { + "label": { "type": "string", - "maxLength": 64 + "maxLength": 200 }, - "body": { - "type": "string" - } - }, - "required": [ - "body", - "version" - ] - }, - "PackMembershipPlan": { - "type": "object", - "properties": { - "name": { + "client_id": { "type": "string", - "maxLength": 120 + "readOnly": true }, - "interval": { - "$ref": "#/components/schemas/IntervalEnum" + "client_type": { + "$ref": "#/components/schemas/ClientTypeEnum" }, - "custom_days": { + "scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "rate_limit_tier": { + "$ref": "#/components/schemas/RateLimitTierEnum" + }, + "makerspace": { "type": "integer", - "maximum": 2147483647, - "minimum": 0, + "readOnly": true, "nullable": true }, - "amount": { + "public_makerspace_code": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "readOnly": true }, - "currency": { + "allowed_origins": { + "type": "array", + "items": { + "type": "string" + } + }, + "backend_base_url": { "type": "string", - "maxLength": 3 + "readOnly": true + }, + "public_api_base_url": { + "type": "string", + "readOnly": true }, "is_active": { "type": "boolean" }, - "loan_max_active": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, + "last_seen_at": { + "type": "string", + "format": "date-time", + "readOnly": true, "nullable": true }, - "loan_max_items": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, + "last_seen_ip": { + "type": "string", + "readOnly": true, "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } - }, - "required": [ - "amount", - "currency", - "custom_days", - "interval", - "is_active", - "loan_max_active", - "loan_max_items", - "name" - ] + } }, - "PackNotificationPreference": { + "PatchedApiIntegrationSettings": { "type": "object", "properties": { - "feature": { - "$ref": "#/components/schemas/Feature4feEnum" + "public_code": { + "type": "string", + "readOnly": true }, - "channel": { - "$ref": "#/components/schemas/ChannelB30Enum" + "public_api_key": { + "type": "string", + "readOnly": true }, - "enabled": { + "cors_allowed_origins": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "telegram_group_chat_id": { + "type": "string", + "maxLength": 64 + }, + "telegram_bot_token": { + "type": "string", + "writeOnly": true + }, + "telegram_bot_token_set": { + "type": "boolean", + "readOnly": true + }, + "smtp_host": { + "type": "string", + "maxLength": 200 + }, + "smtp_port": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "smtp_username": { + "type": "string", + "maxLength": 200 + }, + "smtp_password": { + "type": "string", + "writeOnly": true + }, + "smtp_password_set": { + "type": "boolean", + "readOnly": true + }, + "smtp_use_tls": { "type": "boolean" - } - }, - "required": [ - "channel", - "enabled", - "feature" - ] - }, - "PackOpeningHours": { - "type": "object", - "properties": { - "timezone": { - "type": "string" }, - "opening_hours": { + "smtp_use_ssl": { + "type": "boolean" + }, + "smtp_from_email": { "oneOf": [ { - "type": "object", - "maxProperties": 0 + "type": "string", + "format": "email", + "maxLength": 254 }, { - "type": "object", - "required": [ - "weekly", - "exceptions" - ], - "additionalProperties": false, - "properties": { - "weekly": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "additionalProperties": false, - "required": [ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun" - ], - "properties": { - "mon": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "tue": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "wed": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "thu": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "fri": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sat": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sun": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - } - } - } - ] - }, - "exceptions": { - "type": "object", - "maxProperties": 366, - "additionalProperties": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." - } - } + "type": "string", + "maxLength": 0 } - ], - "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." + ] + }, + "slack_webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2048 + }, + "slack_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "mattermost_webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2048 + }, + "mattermost_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "discord_webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2048 + }, + "discord_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "default_loan_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 } - }, - "required": [ - "opening_hours", - "timezone" - ] + } }, - "PackPreview": { + "PatchedAssignOperator": { "type": "object", "properties": { - "base_checksum": { - "type": "string" - }, - "applied": { - "type": "boolean" - }, - "changes": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackChange" - } + "user_id": { + "type": "integer" + }, + "access_level": { + "type": "string", + "maxLength": 16 } - }, - "required": [ - "applied", - "base_checksum", - "changes" - ] + } }, - "PackProfile": { + "PatchedBackupDestination": { "type": "object", + "description": "Credentials are write-only; reads report only whether they are stored.", "properties": { "id": { "type": "integer", @@ -89330,1501 +111549,1372 @@ }, "name": { "type": "string", - "maxLength": 120 - }, - "pack": { - "$ref": "#/components/schemas/SpacePack" - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 64, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "updated_at": { - "type": "string", - "format": "date-time", + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/BackupDestinationKindEnum" + } + ], "readOnly": true - } - }, - "required": [ - "created_at", - "id", - "name", - "pack", - "updated_at" - ] - }, - "PackSettings": { - "type": "object", - "description": "Schema only: actual writes validate through MakerspaceSerializer.", - "properties": { - "public_inventory_enabled": { - "type": "boolean" - }, - "public_site_enabled": { - "type": "boolean" }, - "public_landing_page": { - "$ref": "#/components/schemas/PublicLandingPage6a0Enum" + "endpoint_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 300 + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "public_stats_enabled": { - "type": "boolean" + "bucket": { + "type": "string", + "maxLength": 255 }, - "public_stats_show_holder_names": { - "type": "boolean" + "region": { + "type": "string", + "maxLength": 64 }, - "public_print_status_lookup_policy": { - "$ref": "#/components/schemas/PublicPrintStatusLookupPolicyEnum" + "prefix": { + "type": "string", + "maxLength": 200 }, - "membership_policy": { - "$ref": "#/components/schemas/MembershipPolicyEnum" + "addressing_style": { + "$ref": "#/components/schemas/AddressingStyleEnum" }, - "membership_dues_amount": { + "access_key_id": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "referrals_enabled": { - "type": "boolean" + "writeOnly": true, + "maxLength": 200 }, - "lapsed_members_cannot_borrow": { - "type": "boolean" + "secret_access_key": { + "type": "string", + "writeOnly": true, + "maxLength": 200 }, - "staff_notifications_enabled": { - "type": "boolean" + "credentials_set": { + "type": "boolean", + "readOnly": true }, - "booking_requester_notifications_enabled": { + "enabled": { "type": "boolean" }, - "default_loan_days": { + "retention_days": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "maximum": 3650, + "minimum": 1 }, - "presence_preset_minutes": {} - }, - "required": [ - "booking_requester_notifications_enabled", - "default_loan_days", - "lapsed_members_cannot_borrow", - "membership_dues_amount", - "membership_policy", - "presence_preset_minutes", - "public_inventory_enabled", - "public_landing_page", - "public_print_status_lookup_policy", - "public_site_enabled", - "public_stats_enabled", - "public_stats_show_holder_names", - "referrals_enabled", - "staff_notifications_enabled" - ] - }, - "PaginatedAdminRequestList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { - "type": "integer", - "example": 123 + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "next": { + "updated_at": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "format": "date-time", + "readOnly": true }, - "previous": { + "retired_at": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "format": "date-time", + "readOnly": true, + "nullable": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AdminRequest" - } + "last_success_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "last_error_code": { + "type": "string", + "readOnly": true } } }, - "PaginatedApiClientList": { + "PatchedBillingSettings": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "payment_terms_days": { "type": "integer", - "example": 123 - }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maximum": 365, + "minimum": 0 }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "reminder_days_before": { + "type": "array", + "items": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "maxItems": 20 }, - "results": { + "reminder_days_after": { "type": "array", "items": { - "$ref": "#/components/schemas/ApiClient" - } - } - } - }, - "PaginatedApiKeyRequestList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "maxItems": 20 + }, + "max_reminders": { "type": "integer", - "example": 123 + "maximum": 20, + "minimum": 0 }, - "next": { + "number_prefix": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 32 }, - "previous": { + "legal_name": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 150 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ApiKeyRequest" - } - } - } - }, - "PaginatedAuditLogList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { - "type": "integer", - "example": 123 + "address": { + "type": "string", + "maxLength": 1000 }, - "next": { + "tax_id": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 64 }, - "previous": { + "footer": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 1000 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AuditLog" - } - } + "account_codes": {} } }, - "PaginatedBindingList": { + "PatchedBindingUpdate": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" - }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "enabled": { + "type": "boolean" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Binding" - } + "auto_apply_terminal": { + "type": "boolean" } } }, - "PaginatedBoxList": { + "PatchedBookableSpaceBookingRules": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "min_booking_duration_minutes": { "type": "integer", - "example": 123 + "maximum": 2147483647, + "minimum": 1 }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "max_booking_duration_minutes": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1 }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "booking_lead_time_minutes": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Box" - } - } - } - }, - "PaginatedCategoryAdminList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "max_booking_advance_days": { "type": "integer", - "example": 123 + "maximum": 2147483647, + "minimum": 1 }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "buffer_before_minutes": { + "type": "integer", + "maximum": 1440, + "minimum": 0 }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "buffer_after_minutes": { + "type": "integer", + "maximum": 1440, + "minimum": 0 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CategoryAdmin" - } + "cancellation_cutoff_hours": { + "type": "integer", + "maximum": 8760, + "minimum": 0 + }, + "approval_mode": { + "$ref": "#/components/schemas/ApprovalModeE35Enum" } } }, - "PaginatedDeviceList": { + "PatchedBookableSpaceWrite": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { + "name": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 200 }, - "previous": { + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/Kind3bfEnum" + } + ], + "default": "other" + }, + "description": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "default": "" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Device" - } - } - } - }, - "PaginatedDirectLoanList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "capacity": { "type": "integer", - "example": 123 + "minimum": 0, + "default": 0 }, - "next": { + "location": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "default": "", + "maxLength": 255 }, - "previous": { + "is_public": { + "type": "boolean", + "default": false + }, + "show_public_availability": { + "type": "boolean", + "default": false + }, + "show_public_booker_names": { + "type": "boolean", + "default": false + }, + "custom_form": { + "nullable": true + }, + "requester_notifications_enabled": { + "type": "boolean", + "nullable": true + }, + "payment_amount": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "default": "0.00" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DirectLoan" - } + "machine_type": { + "type": "integer", + "minimum": 1, + "nullable": true } } }, - "PaginatedDirectLoanMemberList": { + "PatchedBookingDetailsInput": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" - }, - "previous": { + "note": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 5000 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DirectLoanMember" - } + "custom_answers": { + "nullable": true } } }, - "PaginatedEmailLogList": { + "PatchedBookingStaffGrantPermissions": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" - }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" - }, - "results": { + "permissions": { "type": "array", "items": { - "$ref": "#/components/schemas/EmailLog" - } + "$ref": "#/components/schemas/Permissions43eEnum" + }, + "maxItems": 4, + "minItems": 1 } } }, - "PaginatedInventoryAssetAdminList": { + "PatchedBox": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "id": { "type": "integer", - "example": 123 + "readOnly": true }, - "next": { + "makerspace": { + "type": "integer", + "readOnly": true + }, + "parent": { + "type": "integer", + "nullable": true + }, + "code": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "readOnly": true }, - "previous": { + "label": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 200 }, - "results": { + "location": { + "type": "string", + "maxLength": 200 + }, + "description": { + "type": "string" + }, + "kit_components": { "type": "array", "items": { - "$ref": "#/components/schemas/InventoryAssetAdmin" - } - } - } - }, - "PaginatedInventoryProductAdminList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { - "type": "integer", - "example": 123 + "$ref": "#/components/schemas/KitComponent" + }, + "maxItems": 50 }, - "next": { - "type": "string", + "is_active": { + "type": "boolean" + }, + "qr_code_id": { + "type": "integer", "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "readOnly": true }, - "previous": { + "created_at": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "format": "date-time", + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/InventoryProductAdmin" - } + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } } }, - "PaginatedJobList": { + "PatchedCategoryAdmin": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "id": { "type": "integer", - "example": 123 + "readOnly": true }, - "next": { + "makerspace": { + "type": "integer", + "readOnly": true + }, + "name": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 100 }, - "previous": { + "slug": { + "oneOf": [ + { + "type": "string", + "pattern": "^[-a-zA-Z0-9_]+$" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "display_order": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "loan_days": { + "type": "integer", + "maximum": 365, + "minimum": 1, + "nullable": true + }, + "max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "nullable": true + }, + "icon": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 50 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Job" - } + "product_count": { + "type": "integer", + "readOnly": true, + "default": 0 + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } } }, - "PaginatedLabelCommandList": { + "PatchedCertificationTypeUpdate": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "id": { "type": "integer", - "example": 123 + "readOnly": true }, - "next": { + "machine_type": { + "type": "integer", + "readOnly": true + }, + "name": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 200 }, - "previous": { + "description": { + "type": "string" + }, + "validity_days": { + "type": "integer", + "maximum": 2147483647, + "minimum": 1, + "nullable": true + }, + "is_required_for_service": { + "type": "boolean" + }, + "is_required_for_booking": { + "type": "boolean" + }, + "is_active": { + "type": "boolean", + "readOnly": true + }, + "created_at": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "format": "date-time", + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LabelCommand" - } + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } } }, - "PaginatedMaterialList": { + "PatchedDeviceUpdate": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" - }, - "previous": { + "name": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 120 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Material" - } + "offline_threshold_seconds": { + "type": "integer", + "maximum": 604800, + "minimum": 1 } } }, - "PaginatedNotificationList": { + "PatchedEmailTemplateUpdate": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { + "subject": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 200 }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "text_body": { + "type": "string" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Notification" - } + "html_body": { + "type": "string" + }, + "is_active": { + "type": "boolean" } } }, - "PaginatedOverdueScheduleList": { + "PatchedEventSeriesWrite": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "registration_audience": { + "$ref": "#/components/schemas/EventSeriesWriteRegistrationAudienceEnum" + }, + "ticket_types": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TicketInput" + }, + "writeOnly": true + }, + "title": { + "type": "string", + "maxLength": 200 + }, + "description": { + "type": "string", + "default": "" + }, + "location": { + "type": "string", + "default": "", + "maxLength": 255 + }, + "location_kind": { + "allOf": [ + { + "$ref": "#/components/schemas/LocationKindEnum" + } + ], + "default": "other" + }, + "custom_form": { + "nullable": true + }, + "capacity": { "type": "integer", - "example": 123 + "minimum": 0, + "default": 0 }, - "next": { + "payment_amount": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "default": "0.00" }, - "previous": { + "registration_requires_approval": { + "type": "boolean", + "default": false + }, + "cancellation_cutoff_hours": { + "type": "integer", + "maximum": 8760, + "minimum": 0, + "nullable": true + }, + "registration_cutoff_lead_minutes": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "is_public": { + "type": "boolean", + "default": false + }, + "recurrence_timezone": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 64 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/OverdueSchedule" - } + "dtstart_local_date": { + "type": "string", + "format": "date" + }, + "dtstart_local_time": { + "type": "string", + "format": "time" + }, + "recurrence_rule": { + "type": "string", + "maxLength": 500 + }, + "duration_minutes": { + "type": "integer", + "minimum": 1 + }, + "effective_from": { + "type": "string", + "format": "date-time", + "writeOnly": true } } }, - "PaginatedPackProfileList": { + "PatchedEventWrite": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "registration_audience": { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + }, + "category_id": { "type": "integer", - "example": 123 + "minimum": 1, + "nullable": true }, - "next": { + "tags": { + "type": "array", + "items": { + "type": "string", + "maxLength": 50 + }, + "maxItems": 20 + }, + "title": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 200 }, - "previous": { + "description": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "default": "" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PackProfile" - } - } - } - }, - "PaginatedPublicMachineDetailList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "starts_at": { + "type": "string", + "format": "date-time" + }, + "ends_at": { + "type": "string", + "format": "date-time" + }, + "timezone_name": { + "type": "string", + "maxLength": 64 + }, + "location": { + "type": "string", + "default": "", + "maxLength": 255 + }, + "location_kind": { + "allOf": [ + { + "$ref": "#/components/schemas/LocationKindEnum" + } + ], + "default": "other" + }, + "custom_form": { + "nullable": true + }, + "capacity": { "type": "integer", - "example": 123 + "minimum": 0, + "default": 0 }, - "next": { + "payment_amount": { "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "default": "0.00" + }, + "is_public": { + "type": "boolean", + "default": false + }, + "show_registration_count": { + "type": "boolean", + "default": true + }, + "registration_requires_approval": { + "type": "boolean", + "default": false + }, + "cancellation_cutoff_hours": { + "type": "integer", + "maximum": 8760, + "minimum": 0, "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "description": "Member cancellation closes this many hours before start; null means start." }, - "previous": { + "registration_cutoff_at": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "format": "date-time", + "nullable": true }, - "results": { + "registration_cutoff_lead_minutes": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "inherit_fields": { "type": "array", "items": { - "$ref": "#/components/schemas/PublicMachineDetail" - } + "$ref": "#/components/schemas/InheritFieldsEnum" + }, + "writeOnly": true } } }, - "PaginatedPublicMachineList": { + "PatchedEvidenceRetentionPatch": { "type": "object", - "required": [ - "count", - "results" - ], + "description": "True partial update: supply any subset; an empty body is rejected.", "properties": { - "count": { + "object_retention_days": { "type": "integer", - "example": 123 - }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maximum": 3650, + "minimum": 30, + "nullable": true }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "abandoned_upload_cleanup_enabled": { + "type": "boolean" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicMachine" - } + "abandoned_upload_retention_days": { + "type": "integer", + "maximum": 3650, + "minimum": 1, + "nullable": true } } }, - "PaginatedPublicProductList": { + "PatchedInventoryAssetAdminUpdate": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { - "type": "integer", - "example": 123 - }, - "next": { + "asset_tag": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 100 }, - "previous": { + "serial_number": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 100 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicProduct" - } + "box": { + "type": "integer", + "nullable": true + }, + "notes": { + "type": "string" + }, + "public_self_checkout_enabled": { + "type": "boolean" } } }, - "PaginatedQrPrintBatchList": { + "PatchedInventoryProductAdminUpdate": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "id": { "type": "integer", - "example": 123 + "readOnly": true }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "makerspace": { + "type": "integer", + "readOnly": true }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "box": { + "type": "integer", + "nullable": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QrPrintBatch" - } - } - } - }, - "PaginatedReorderSuggestionList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "category": { "type": "integer", - "example": 123 + "nullable": true }, - "next": { + "name": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 200 }, - "previous": { + "manufacturer": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 200 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReorderSuggestion" - } - } - } - }, - "PaginatedReplayRecoveryList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { - "type": "integer", - "example": 123 + "model": { + "type": "string", + "maxLength": 200 }, - "next": { + "sku": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 100 }, - "previous": { + "barcode": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 100 }, - "results": { + "supplier": { + "type": "string", + "maxLength": 200 + }, + "reorder_point": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "reorder_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "kit_components": { "type": "array", "items": { - "$ref": "#/components/schemas/ReplayRecovery" - } - } - } - }, - "PaginatedReportScheduleList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { - "type": "integer", - "example": 123 + "$ref": "#/components/schemas/KitComponent" + }, + "maxItems": 50 }, - "next": { + "description": { + "type": "string" + }, + "unit_cost": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,4})?$", + "nullable": true }, - "previous": { + "cost_currency": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 3 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReportSchedule" - } - } - } - }, - "PaginatedScheduleList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { - "type": "integer", - "example": 123 + "cost_unit": { + "type": "string", + "maxLength": 16 }, - "next": { + "borrowing_terms": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 2000 }, - "previous": { + "image_key": { + "type": "string", + "readOnly": true + }, + "image_url": { "type": "string", - "nullable": true, "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "nullable": true, + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Schedule" - } - } - } - }, - "PaginatedStaffInvitationList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "tracking_mode": { + "$ref": "#/components/schemas/TrackingModeB86Enum" + }, + "loan_days": { "type": "integer", - "example": 123 + "maximum": 365, + "minimum": 1, + "nullable": true }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "nullable": true }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "total_quantity": { + "type": "integer", + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/StaffInvitation" - } - } - } - }, - "PaginatedStaffMembershipList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "available_quantity": { "type": "integer", - "example": 123 + "readOnly": true }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "reserved_quantity": { + "type": "integer", + "readOnly": true }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "issued_quantity": { + "type": "integer", + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/StaffMembership" - } - } - } - }, - "PaginatedStockTransferList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "damaged_quantity": { "type": "integer", - "example": 123 + "readOnly": true }, - "next": { + "lost_quantity": { + "type": "integer", + "readOnly": true + }, + "needs_fix_quantity": { + "type": "integer", + "readOnly": true + }, + "is_public": { + "type": "boolean" + }, + "public_self_checkout_enabled": { + "type": "boolean" + }, + "show_public_count": { + "type": "boolean" + }, + "public_availability_mode": { + "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" + }, + "storage_location": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 200 }, - "previous": { + "is_archived": { + "type": "boolean" + }, + "created_at": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "format": "date-time", + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/StockTransfer" - } + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } } }, - "PaginatedStocktakeList": { + "PatchedMachine": { "type": "object", - "required": [ - "count", - "results" - ], "properties": { - "count": { + "id": { "type": "integer", - "example": 123 + "readOnly": true }, - "next": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "makerspace": { + "type": "integer", + "readOnly": true }, - "previous": { - "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "machine_type": { + "allOf": [ + { + "$ref": "#/components/schemas/MachineType" + } + ], + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Stocktake" - } - } - } - }, - "PaginatedSubdomainRequestList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { + "machine_type_id": { "type": "integer", - "example": 123 + "writeOnly": true }, - "next": { + "name": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "maxLength": 200 }, - "previous": { + "location": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "maxLength": 200 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SubdomainRequest" - } - } - } - }, - "PaginatedWarrantyReportRowList": { - "type": "object", - "required": [ - "count", - "results" - ], - "properties": { - "count": { - "type": "integer", - "example": 123 + "notes": { + "type": "string" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/MachineStatusEnum" + } + ], + "readOnly": true + }, + "firmware_version": { + "type": "string", + "maxLength": 100 + }, + "camera_feed_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "next": { + "status_since": { "type": "string", - "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=4" + "format": "date-time", + "readOnly": true }, - "previous": { + "public_fields": {}, + "type_payload": {}, + "image_url": { "type": "string", "nullable": true, - "format": "uri", - "example": "http://api.example.org/accounts/?page=2" + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WarrantyReportRow" - } - } - } - }, - "Pairing": { - "type": "object", - "properties": { - "id": { + "warranty_status": { "type": "string", - "format": "uuid", "readOnly": true }, - "migration_id": { - "type": "string", - "format": "uuid" + "is_public": { + "type": "boolean", + "readOnly": true }, - "source_tenant_id": { - "type": "string", - "maxLength": 64 + "is_active": { + "type": "boolean", + "readOnly": true }, - "archive_digest": { - "type": "string", - "maxLength": 64 + "usage_hours": { + "type": "number", + "format": "double", + "readOnly": true }, - "source_deployment_id": { - "type": "string", - "maxLength": 128 + "can_operate": { + "type": "boolean", + "readOnly": true }, - "source_fingerprint": { - "type": "string", - "maxLength": 64 + "can_edit": { + "type": "boolean", + "readOnly": true }, - "target_deployment_id": { - "type": "string", - "maxLength": 128 + "can_delegate": { + "type": "boolean", + "readOnly": true }, - "target_fingerprint": { - "type": "string", - "maxLength": 64 + "can_retire": { + "type": "boolean", + "readOnly": true }, - "approved_at": { - "type": "string", - "format": "date-time", + "can_unretire": { + "type": "boolean", "readOnly": true - } - }, - "required": [ - "approved_at", - "archive_digest", - "id", - "migration_id", - "source_deployment_id", - "source_fingerprint", - "source_tenant_id", - "target_deployment_id", - "target_fingerprint" - ] - }, - "PairingCreate": { - "type": "object", - "properties": { - "migration_id": { - "type": "string", - "format": "uuid" }, - "source_tenant_id": { - "type": "string", - "maxLength": 64 + "can_manage": { + "type": "boolean", + "readOnly": true }, - "archive_digest": { + "created_at": { "type": "string", - "pattern": "^[0-9a-f]{64}$" - }, - "source": { - "type": "object", - "additionalProperties": {} - }, - "target": { - "type": "object", - "additionalProperties": {} - } - }, - "required": [ - "archive_digest", - "migration_id", - "source", - "source_tenant_id", - "target" - ] - }, - "PaperSizeEnum": { - "enum": [ - "A4", - "LETTER", - "custom" - ], - "type": "string", - "description": "* `A4` - A4\n* `LETTER` - LETTER\n* `custom` - custom" - }, - "PasswordLoginResult": { - "oneOf": [ - { - "$ref": "#/components/schemas/LoginResponse" + "format": "date-time", + "readOnly": true }, - { - "$ref": "#/components/schemas/MFAChallengeResponse" - } - ] - }, - "PasswordResetAcknowledgement": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - }, - "required": [ - "detail" - ] - }, - "PasswordResetFailure": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - }, - "required": [ - "detail" - ] - }, - "PasswordUpdated": { - "type": "object", - "properties": { - "detail": { - "type": "string" + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } - }, - "required": [ - "detail" - ] + } }, - "PasswordValidationFailure": { + "PatchedMachinePublicity": { "type": "object", "properties": { - "new_password": { - "type": "array", - "items": { - "type": "string" - } + "is_public": { + "type": "boolean" } - }, - "required": [ - "new_password" - ] + } }, - "PatchedAccountProfile": { + "PatchedMachineTypeUpdate": { "type": "object", "properties": { - "display_name": { + "name": { "type": "string", "maxLength": 200 }, - "first_name": { - "type": "string", - "maxLength": 150 - }, - "last_name": { + "icon": { "type": "string", - "maxLength": 150 + "maxLength": 50 }, - "phone": { - "type": "string", - "maxLength": 32 - } + "capability_config": {} } }, - "PatchedAdmissionSettings": { + "PatchedMaintenanceScheduleWrite": { "type": "object", "properties": { - "configured": { - "type": "boolean", - "readOnly": true - }, - "join_mode": { - "$ref": "#/components/schemas/JoinModeEnum" + "description": { + "type": "string" }, - "vouches_required": { + "interval_days": { "type": "integer", - "maximum": 5, "minimum": 1 }, - "voucher_min_tenure_days": { - "type": "integer", - "maximum": 3650, - "minimum": 0 + "next_due": { + "type": "string", + "format": "date" }, - "voucher_pending_limit": { + "trigger": { + "$ref": "#/components/schemas/MaintenanceScheduleWriteTriggerEnum" + }, + "usage_hours": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "job_count": { "type": "integer", - "maximum": 100, - "minimum": 1 + "minimum": 1, + "nullable": true }, - "vouch_expiry_days": { + "anchor": { + "$ref": "#/components/schemas/AnchorEnum" + } + } + }, + "PatchedMakerspace": { + "type": "object", + "properties": { + "organization_id": { "type": "integer", - "maximum": 365, - "minimum": 1 + "minimum": 1, + "writeOnly": true, + "description": "Existing active owner organization ID. On create, supply this or organization_name, exactly one; refused on update." }, - "vouch_replaces_approval": { - "type": "boolean" + "organization_name": { + "type": "string", + "writeOnly": true, + "description": "Create a new owner organization with this name. On create, supply this or organization_id, exactly one; refused on update.", + "maxLength": 200 }, - "payment_timing": { - "$ref": "#/components/schemas/PaymentTimingEnum" + "creation_setup": { + "allOf": [ + { + "$ref": "#/components/schemas/MakerspaceCreationSetup" + } + ], + "writeOnly": true }, - "application_schema": { + "creation_invitation": { + "allOf": [ + { + "$ref": "#/components/schemas/CreationInviteDelivery" + } + ], + "readOnly": true, "nullable": true }, - "default_plan": { + "install_profile_id": { "type": "integer", - "nullable": true + "minimum": 1, + "writeOnly": true }, - "joining_fee": { + "id": { + "type": "integer", + "readOnly": true + }, + "name": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "maxLength": 200 }, - "joining_fee_currency": { + "public_code": { "type": "string", - "pattern": "^[A-Za-z]{3}$" + "pattern": "^[A-Z0-9]{4}$", + "maxLength": 4 }, - "rejoin_dues_rule": { - "$ref": "#/components/schemas/RejoinDuesRuleEnum" + "slug": { + "type": "string", + "maxLength": 50, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "request_expiry_days": { - "type": "integer", - "maximum": 365, - "minimum": 1 + "location": { + "type": "string", + "maxLength": 200 }, - "recipe": { - "allOf": [ + "timezone": { + "type": "string", + "readOnly": true + }, + "country": { + "type": "string", + "readOnly": true + }, + "opening_hours": { + "oneOf": [ { - "$ref": "#/components/schemas/RecipeEnum" + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "required": [ + "weekly", + "exceptions" + ], + "additionalProperties": false, + "properties": { + "weekly": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + "sun" + ], + "properties": { + "mon": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "tue": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "wed": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "thu": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "fri": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sat": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sun": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + } + } + } + ] + }, + "exceptions": { + "type": "object", + "maxProperties": 366, + "additionalProperties": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." + } + } } ], - "writeOnly": true - } - } - }, - "PatchedApiClient": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "label": { - "type": "string", - "maxLength": 200 - }, - "client_id": { - "type": "string", - "readOnly": true - }, - "client_type": { - "$ref": "#/components/schemas/ClientTypeEnum" - }, - "scopes": { - "type": "array", - "items": { - "type": "string" - } - }, - "rate_limit_tier": { - "$ref": "#/components/schemas/RateLimitTierEnum" - }, - "makerspace": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "public_makerspace_code": { - "type": "string", - "readOnly": true - }, - "allowed_origins": { - "type": "array", - "items": { - "type": "string" - } - }, - "backend_base_url": { - "type": "string", - "readOnly": true - }, - "public_api_base_url": { - "type": "string", - "readOnly": true - }, - "is_active": { - "type": "boolean" - }, - "last_seen_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "last_seen_ip": { - "type": "string", - "readOnly": true, - "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true - } - } - }, - "PatchedApiIntegrationSettings": { - "type": "object", - "properties": { - "public_code": { - "type": "string", - "readOnly": true - }, - "public_api_key": { - "type": "string", - "readOnly": true - }, - "cors_allowed_origins": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true - }, - "telegram_group_chat_id": { - "type": "string", - "maxLength": 64 - }, - "telegram_bot_token": { - "type": "string", - "writeOnly": true - }, - "telegram_bot_token_set": { - "type": "boolean", - "readOnly": true - }, - "smtp_host": { - "type": "string", - "maxLength": 200 - }, - "smtp_port": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "smtp_username": { - "type": "string", - "maxLength": 200 - }, - "smtp_password": { - "type": "string", - "writeOnly": true - }, - "smtp_password_set": { - "type": "boolean", + "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days.", "readOnly": true }, - "smtp_use_tls": { - "type": "boolean" - }, - "smtp_use_ssl": { - "type": "boolean" - }, - "smtp_from_email": { + "map_url": { "oneOf": [ { "type": "string", - "format": "email", - "maxLength": 254 + "format": "uri", + "maxLength": 200 }, { "type": "string", @@ -90832,397 +112922,282 @@ } ] }, - "slack_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 - }, - "slack_webhook_url_set": { - "type": "boolean", - "readOnly": true - }, - "mattermost_webhook_url": { + "geofence_latitude": { "type": "string", - "writeOnly": true, - "maxLength": 2048 - }, - "mattermost_webhook_url_set": { - "type": "boolean", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,3}(?:\\.\\d{0,6})?$", + "nullable": true }, - "discord_webhook_url": { + "geofence_longitude": { "type": "string", - "writeOnly": true, - "maxLength": 2048 - }, - "discord_webhook_url_set": { - "type": "boolean", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,3}(?:\\.\\d{0,6})?$", + "nullable": true }, - "default_loan_days": { + "geofence_radius_m": { "type": "integer", "maximum": 2147483647, - "minimum": 0 - } - } - }, - "PatchedAssignOperator": { - "type": "object", - "properties": { - "user_id": { - "type": "integer" - }, - "access_level": { - "type": "string", - "maxLength": 16 - } - } - }, - "PatchedBackupDestination": { - "type": "object", - "description": "Credentials are write-only; reads report only whether they are stored.", - "properties": { - "id": { - "type": "integer", - "readOnly": true + "minimum": 1 }, - "name": { - "type": "string", - "maxLength": 64, - "pattern": "^[-a-zA-Z0-9_]+$" + "geofence_enabled": { + "type": "boolean" }, - "kind": { - "allOf": [ - { - "$ref": "#/components/schemas/BackupDestinationKindEnum" - } - ], - "readOnly": true + "public_inventory_enabled": { + "type": "boolean" }, - "endpoint_url": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 300 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "public_site_enabled": { + "type": "boolean" }, - "bucket": { - "type": "string", - "maxLength": 255 + "public_landing_page": { + "$ref": "#/components/schemas/PublicLandingPage6a0Enum" }, - "region": { - "type": "string", - "maxLength": 64 + "public_stats_enabled": { + "type": "boolean" }, - "prefix": { - "type": "string", - "maxLength": 200 + "public_stats_show_holder_names": { + "type": "boolean" }, - "addressing_style": { - "$ref": "#/components/schemas/AddressingStyleEnum" + "public_print_status_lookup_policy": { + "$ref": "#/components/schemas/PublicPrintStatusLookupPolicyEnum" }, - "access_key_id": { - "type": "string", - "writeOnly": true, - "maxLength": 200 + "membership_policy": { + "$ref": "#/components/schemas/MembershipPolicyEnum" }, - "secret_access_key": { + "membership_dues_amount": { "type": "string", - "writeOnly": true, - "maxLength": 200 + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "credentials_set": { - "type": "boolean", - "readOnly": true + "referrals_enabled": { + "type": "boolean" }, - "enabled": { + "lapsed_members_cannot_borrow": { "type": "boolean" }, - "retention_days": { - "type": "integer", - "maximum": 3650, - "minimum": 1 + "anonymous_requests_enabled": { + "type": "boolean" }, - "created_at": { + "request_access": { "type": "string", - "format": "date-time", "readOnly": true }, - "updated_at": { + "filament_low_stock_threshold_grams": { "type": "string", - "format": "date-time", + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$" + }, + "superadmin_access_enabled": { + "type": "boolean" + }, + "archive_custody_state": { + "allOf": [ + { + "$ref": "#/components/schemas/ArchiveCustodyStateEnum" + } + ], + "nullable": true, "readOnly": true }, - "retired_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "staff_notifications_enabled": { + "type": "boolean" }, - "last_success_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "booking_requester_notifications_enabled": { + "type": "boolean" }, - "last_error_code": { - "type": "string", - "readOnly": true - } - } - }, - "PatchedBillingSettings": { - "type": "object", - "properties": { - "payment_terms_days": { - "type": "integer", - "maximum": 365, - "minimum": 0 + "event_default_registration_audience": { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" }, - "reminder_days_before": { + "event_reminder_lead_hours": { "type": "array", "items": { "type": "integer", - "maximum": 365, + "maximum": 8760, "minimum": 1 }, - "maxItems": 20 + "maxItems": 100 }, - "reminder_days_after": { + "booking_reminder_lead_hours": { "type": "array", "items": { "type": "integer", - "maximum": 365, + "maximum": 8760, "minimum": 1 }, - "maxItems": 20 - }, - "max_reminders": { - "type": "integer", - "maximum": 20, - "minimum": 0 - }, - "number_prefix": { - "type": "string", - "maxLength": 32 + "maxItems": 100 }, - "legal_name": { + "logo_key": { "type": "string", - "maxLength": 150 + "readOnly": true }, - "address": { + "logo_url": { "type": "string", - "maxLength": 1000 + "format": "uri", + "nullable": true, + "readOnly": true }, - "tax_id": { + "cover_image_key": { "type": "string", - "maxLength": 64 + "readOnly": true }, - "footer": { + "cover_image_url": { "type": "string", - "maxLength": 1000 - }, - "account_codes": {} - } - }, - "PatchedBindingUpdate": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "auto_apply_terminal": { - "type": "boolean" - } - } - }, - "PatchedBookableSpaceBookingRules": { - "type": "object", - "properties": { - "min_booking_duration_minutes": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1 - }, - "max_booking_duration_minutes": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1 - }, - "booking_lead_time_minutes": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "max_booking_advance_days": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1 - }, - "buffer_before_minutes": { - "type": "integer", - "maximum": 1440, - "minimum": 0 - }, - "buffer_after_minutes": { - "type": "integer", - "maximum": 1440, - "minimum": 0 - }, - "cancellation_cutoff_hours": { - "type": "integer", - "maximum": 8760, - "minimum": 0 + "format": "uri", + "nullable": true, + "readOnly": true }, - "approval_mode": { - "$ref": "#/components/schemas/ApprovalModeE35Enum" - } - } - }, - "PatchedBookableSpaceWrite": { - "type": "object", - "properties": { - "name": { + "frontend_domain": { "type": "string", - "maxLength": 200 + "nullable": true, + "maxLength": 255 }, - "kind": { + "frontend_domain_status": { "allOf": [ { - "$ref": "#/components/schemas/Kind3bfEnum" + "$ref": "#/components/schemas/FrontendDomainStatusEnum" } ], - "default": "other" + "readOnly": true }, - "description": { + "domain_verified_at": { "type": "string", - "default": "" - }, - "capacity": { - "type": "integer", - "minimum": 0, - "default": 0 + "format": "date-time", + "readOnly": true, + "nullable": true }, - "location": { + "domain_verification_token": { "type": "string", - "default": "", - "maxLength": 255 + "readOnly": true }, - "is_public": { - "type": "boolean", - "default": false + "domain_verification_record": { + "type": "object", + "nullable": true, + "properties": { + "host": { + "type": "string" + }, + "type": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "readOnly": true }, - "show_public_availability": { + "platform_hosting": { "type": "boolean", - "default": false + "readOnly": true }, - "show_public_booker_names": { + "is_platform_subdomain": { "type": "boolean", - "default": false - }, - "custom_form": { - "nullable": true + "readOnly": true }, - "requester_notifications_enabled": { - "type": "boolean", - "nullable": true + "hidden_from_central_directory": { + "type": "boolean" }, - "payment_amount": { + "public_api_key": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "default": "0.00" + "readOnly": true }, - "machine_type": { - "type": "integer", - "minimum": 1, - "nullable": true - } - } - }, - "PatchedBox": { - "type": "object", - "properties": { - "id": { - "type": "integer", + "cors_allowed_origins": { "readOnly": true }, - "makerspace": { - "type": "integer", + "enabled_modules": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true }, - "parent": { - "type": "integer", - "nullable": true + "unavailable_apps": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true }, - "code": { - "type": "string", + "resource_limit_overrides": {}, + "enabled_features": {}, + "theme_config": { + "type": "object", + "additionalProperties": false, + "description": "Public/member brand colours. Labels and light/dark hover, active, soft and link colours are derived automatically with accessible fallbacks. Well-formed #RGB or #RRGGBB colours are accepted; only malformed colour values are rejected. Blank colours use Maker Teal. Staff chrome and status colours stay fixed.", + "properties": { + "mode": { + "type": "string", + "enum": [ + "system", + "light", + "dark" + ] + }, + "primary_color": { + "type": "string", + "pattern": "^$|^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + }, + "accent_color": { + "type": "string", + "pattern": "^$|^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + }, + "logo_url": { + "type": "string", + "maxLength": 2048 + } + } + }, + "branding_config": { "readOnly": true }, - "label": { + "public_display_name": { "type": "string", + "writeOnly": true, "maxLength": 200 }, - "location": { + "telegram_group_chat_id": { "type": "string", - "maxLength": 200 + "maxLength": 64 }, - "description": { - "type": "string" + "telegram_bot_token": { + "type": "string", + "writeOnly": true }, - "kit_components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitComponent" - }, - "maxItems": 50 + "telegram_bot_token_set": { + "type": "boolean", + "readOnly": true }, - "is_active": { - "type": "boolean" + "smtp_host": { + "type": "string", + "maxLength": 200 }, - "qr_code_id": { + "smtp_port": { "type": "integer", - "nullable": true, - "readOnly": true + "maximum": 2147483647, + "minimum": 0 }, - "created_at": { + "smtp_username": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 200 }, - "updated_at": { + "smtp_password": { "type": "string", - "format": "date-time", - "readOnly": true - } - } - }, - "PatchedCategoryAdmin": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true + "writeOnly": true }, - "makerspace": { - "type": "integer", + "smtp_password_set": { + "type": "boolean", "readOnly": true }, - "name": { - "type": "string", - "maxLength": 100 + "smtp_use_tls": { + "type": "boolean" }, - "slug": { + "smtp_use_ssl": { + "type": "boolean" + }, + "smtp_from_email": { "oneOf": [ { "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" + "format": "email", + "maxLength": 254 }, { "type": "string", @@ -91230,32 +113205,39 @@ } ] }, - "display_order": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "slack_webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2048 }, - "loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1, - "nullable": true + "slack_webhook_url_set": { + "type": "boolean", + "readOnly": true }, - "max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0, - "nullable": true + "mattermost_webhook_url": { + "type": "string", + "writeOnly": true, + "maxLength": 2048 }, - "icon": { + "mattermost_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "discord_webhook_url": { "type": "string", - "maxLength": 50 + "writeOnly": true, + "maxLength": 2048 }, - "product_count": { + "discord_webhook_url_set": { + "type": "boolean", + "readOnly": true + }, + "default_loan_days": { "type": "integer", - "readOnly": true, - "default": 0 + "maximum": 2147483647, + "minimum": 0 }, + "presence_preset_minutes": {}, "created_at": { "type": "string", "format": "date-time", @@ -91268,464 +113250,697 @@ } } }, - "PatchedCertificationTypeUpdate": { + "PatchedMakerspacePaymentSettings": { "type": "object", "properties": { - "id": { - "type": "integer", + "default_currency": { + "type": "string", + "maxLength": 3 + }, + "stripe_publishable_key": { + "type": "string", + "writeOnly": true, + "maxLength": 255 + }, + "stripe_publishable_key_set": { + "type": "boolean", "readOnly": true }, - "machine_type": { - "type": "integer", + "stripe_secret_key": { + "type": "string", + "writeOnly": true + }, + "stripe_secret_key_set": { + "type": "boolean", "readOnly": true }, - "name": { + "stripe_webhook_secret": { "type": "string", - "maxLength": 200 + "writeOnly": true }, - "description": { - "type": "string" + "stripe_webhook_secret_set": { + "type": "boolean", + "readOnly": true }, - "validity_days": { - "type": "integer", - "maximum": 2147483647, - "minimum": 1, + "connect_account_id": { + "type": "string", + "readOnly": true, "nullable": true }, - "is_required_for_service": { - "type": "boolean" + "connect_status": { + "allOf": [ + { + "$ref": "#/components/schemas/ConnectStatusEnum" + } + ], + "readOnly": true }, - "is_required_for_booking": { - "type": "boolean" + "connect_charges_enabled": { + "type": "boolean", + "readOnly": true }, - "is_active": { + "connect_payouts_enabled": { "type": "boolean", "readOnly": true }, - "created_at": { + "connect_status_updated_at": { "type": "string", "format": "date-time", "readOnly": true }, - "updated_at": { + "effective_mode": { "type": "string", - "format": "date-time", + "readOnly": true + }, + "loan_deposit_mode": { + "$ref": "#/components/schemas/LoanDepositModeEnum" + }, + "loan_deposit_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "loan_late_fee_per_day": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "loan_late_fee_cap": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "loan_grace_days": { + "type": "integer", + "maximum": 32767, + "minimum": 0 + }, + "loan_deposit_blocks_issue": { + "type": "boolean" + } + } + }, + "PatchedMakerspaceProfile": { + "type": "object", + "properties": { + "timezone": { + "type": "string", + "maxLength": 100 + }, + "country": { + "type": "string", + "maxLength": 2 + }, + "opening_hours": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "required": [ + "weekly", + "exceptions" + ], + "additionalProperties": false, + "properties": { + "weekly": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + "sun" + ], + "properties": { + "mon": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "tue": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "wed": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "thu": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "fri": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sat": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sun": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + } + } + } + ] + }, + "exceptions": { + "type": "object", + "maxProperties": 366, + "additionalProperties": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." + } + } + } + ], + "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." + }, + "default_currency": { + "type": "string", + "maxLength": 3 + }, + "timezone_choices": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "country_choices": { + "type": "array", + "items": { + "type": "string" + }, "readOnly": true } } }, - "PatchedDeviceUpdate": { + "PatchedMaterial": { "type": "object", "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "machine_type": { + "type": "integer" + }, "name": { "type": "string", - "maxLength": 120 + "maxLength": 100 }, - "offline_threshold_seconds": { - "type": "integer", - "maximum": 604800, - "minimum": 1 + "colours": {}, + "is_active": { + "type": "boolean" } } }, - "PatchedEmailTemplateUpdate": { + "PatchedMemberCardOwnUpdate": { "type": "object", "properties": { - "subject": { + "printed_name": { "type": "string", "maxLength": 200 + } + } + }, + "PatchedMembershipCapabilities": { + "type": "object", + "properties": { + "can_refer": { + "type": "boolean" }, - "text_body": { - "type": "string" - }, - "html_body": { - "type": "string" - }, - "is_active": { + "can_verify": { "type": "boolean" } } }, - "PatchedEventSeriesWrite": { + "PatchedMembershipPlan": { "type": "object", "properties": { - "registration_audience": { - "$ref": "#/components/schemas/EventSeriesWriteRegistrationAudienceEnum" - }, - "ticket_types": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TicketInput" - }, - "writeOnly": true - }, - "title": { - "type": "string", - "maxLength": 200 - }, - "description": { - "type": "string", - "default": "" + "id": { + "type": "integer", + "readOnly": true }, - "location": { + "name": { "type": "string", - "default": "", - "maxLength": 255 - }, - "location_kind": { - "allOf": [ - { - "$ref": "#/components/schemas/LocationKindEnum" - } - ], - "default": "other" + "maxLength": 120 }, - "custom_form": { - "nullable": true + "interval": { + "$ref": "#/components/schemas/IntervalEnum" }, - "capacity": { + "custom_days": { "type": "integer", + "maximum": 2147483647, "minimum": 0, - "default": 0 + "nullable": true }, - "payment_amount": { + "amount": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "default": "0.00" + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "registration_requires_approval": { - "type": "boolean", - "default": false + "currency": { + "type": "string", + "maxLength": 3 }, - "cancellation_cutoff_hours": { + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "loan_max_active": { "type": "integer", - "maximum": 8760, + "maximum": 2147483647, "minimum": 0, "nullable": true }, - "registration_cutoff_lead_minutes": { + "loan_max_items": { "type": "integer", + "maximum": 2147483647, "minimum": 0, "nullable": true }, - "is_public": { - "type": "boolean", - "default": false - }, - "recurrence_timezone": { - "type": "string", - "maxLength": 64 - }, - "dtstart_local_date": { - "type": "string", - "format": "date" + "auto_renew": { + "type": "boolean" }, - "dtstart_local_time": { - "type": "string", - "format": "time" + "renewal_open_days": { + "type": "integer", + "maximum": 365, + "minimum": 0 }, - "recurrence_rule": { - "type": "string", - "maxLength": 500 + "renewal_grace_days": { + "type": "integer", + "maximum": 365, + "minimum": 0 }, - "duration_minutes": { + "renewal_retries": { "type": "integer", - "minimum": 1 + "maximum": 3, + "minimum": 0 }, - "effective_from": { - "type": "string", - "format": "date-time", - "writeOnly": true - } + "reminder_days_before": {}, + "reminder_days_after": {} } }, - "PatchedEventWrite": { + "PatchedMembershipRoleAssign": { "type": "object", "properties": { - "registration_audience": { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" - }, - "category_id": { + "role_id": { "type": "integer", - "minimum": 1, - "nullable": true + "description": "Single-role set; omit role_ids." }, - "tags": { + "role_ids": { "type": "array", "items": { - "type": "string", - "maxLength": 50 + "type": "integer" }, - "maxItems": 20 - }, - "title": { - "type": "string", - "maxLength": 200 - }, - "description": { - "type": "string", - "default": "" - }, - "starts_at": { - "type": "string", - "format": "date-time" + "description": "Complete role set. Space Manager and Member must each be assigned alone." + } + } + }, + "PatchedMessagingSettings": { + "type": "object", + "properties": { + "use_platform_sender": { + "type": "boolean" }, - "ends_at": { - "type": "string", - "format": "date-time" + "sms_account_sid": { + "oneOf": [ + { + "type": "string", + "pattern": "^(AC[0-9a-fA-F]{32})?$" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "timezone_name": { + "sms_from_number": { "type": "string", - "maxLength": 64 + "maxLength": 32 }, - "location": { + "sms_auth_token": { "type": "string", - "default": "", - "maxLength": 255 + "writeOnly": true, + "maxLength": 4096 }, - "location_kind": { - "allOf": [ + "whatsapp_phone_number_id": { + "oneOf": [ { - "$ref": "#/components/schemas/LocationKindEnum" + "type": "string", + "pattern": "^\\d{1,64}$" + }, + { + "type": "string", + "maxLength": 0 } - ], - "default": "other" + ] }, - "custom_form": { - "nullable": true + "whatsapp_business_account_id": { + "oneOf": [ + { + "type": "string", + "pattern": "^\\d{1,64}$" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "capacity": { - "type": "integer", - "minimum": 0, - "default": 0 + "whatsapp_access_token": { + "type": "string", + "writeOnly": true, + "maxLength": 8192 }, - "payment_amount": { + "whatsapp_app_secret": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "default": "0.00" + "writeOnly": true, + "maxLength": 4096 }, - "is_public": { + "sms_auth_token_set": { "type": "boolean", - "default": false + "readOnly": true }, - "registration_requires_approval": { + "whatsapp_access_token_set": { "type": "boolean", - "default": false + "readOnly": true }, - "cancellation_cutoff_hours": { - "type": "integer", - "maximum": 8760, - "minimum": 0, - "nullable": true, - "description": "Member cancellation closes this many hours before start; null means start." + "whatsapp_app_secret_set": { + "type": "boolean", + "readOnly": true }, - "registration_cutoff_at": { + "whatsapp_webhook_verify_token": { "type": "string", - "format": "date-time", - "nullable": true + "readOnly": true }, - "registration_cutoff_lead_minutes": { - "type": "integer", - "minimum": 0, - "nullable": true + "effective_source": { + "type": "object", + "additionalProperties": { + "enum": [ + "own", + "platform", + "none" + ], + "type": "string", + "description": "* `own` - own\n* `platform` - platform\n* `none` - none" + }, + "readOnly": true }, - "inherit_fields": { + "platform_available": { + "type": "object", + "additionalProperties": { + "type": "boolean" + }, + "readOnly": true + }, + "webhook_urls": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "readOnly": true + } + } + }, + "PatchedNotificationRecipientsPatch": { + "type": "object", + "properties": { + "recipients": { "type": "array", "items": { - "$ref": "#/components/schemas/InheritFieldsEnum" - }, - "writeOnly": true + "$ref": "#/components/schemas/NotificationRecipientUpdate" + } } } }, - "PatchedEvidenceRetentionPatch": { + "PatchedNotificationRulesPatch": { "type": "object", - "description": "True partial update: supply any subset; an empty body is rejected.", "properties": { - "object_retention_days": { - "type": "integer", - "maximum": 3650, - "minimum": 30, - "nullable": true - }, - "abandoned_upload_cleanup_enabled": { - "type": "boolean" + "changes": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationRuleChange" + } }, - "abandoned_upload_retention_days": { - "type": "integer", - "maximum": 3650, - "minimum": 1, - "nullable": true + "preferences": { + "type": "array", + "items": { + "$ref": "#/components/schemas/NotificationPreferenceChange" + } } } }, - "PatchedInventoryAssetAdminUpdate": { + "PatchedOrganizationMembershipUpdate": { "type": "object", "properties": { - "asset_tag": { - "type": "string", - "maxLength": 100 - }, - "serial_number": { - "type": "string", - "maxLength": 100 - }, - "box": { - "type": "integer", - "nullable": true + "status": { + "$ref": "#/components/schemas/StatusE75Enum" }, - "notes": { - "type": "string" + "governance_actions": { + "type": "array", + "items": { + "type": "string" + } }, - "public_self_checkout_enabled": { - "type": "boolean" + "granted_actions": { + "type": "array", + "items": { + "type": "string" + } } } }, - "PatchedInventoryProductAdminUpdate": { + "PatchedOrganizationProfileUpdate": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer", - "readOnly": true - }, - "box": { - "type": "integer", - "nullable": true - }, - "category": { - "type": "integer", - "nullable": true - }, "name": { "type": "string", "maxLength": 200 }, - "manufacturer": { - "type": "string", - "maxLength": 200 - }, - "model": { - "type": "string", - "maxLength": 200 - }, - "sku": { - "type": "string", - "maxLength": 100 - }, - "barcode": { - "type": "string", - "maxLength": 100 - }, - "supplier": { + "slug": { "type": "string", - "maxLength": 200 - }, - "reorder_point": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "reorder_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "kit_components": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitComponent" - }, - "maxItems": 50 + "maxLength": 50, + "pattern": "^[-a-zA-Z0-9_]+$" }, "description": { "type": "string" }, - "borrowing_terms": { - "type": "string", - "maxLength": 2000 - }, - "image_key": { - "type": "string", - "readOnly": true - }, - "image_url": { - "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true - }, - "tracking_mode": { - "$ref": "#/components/schemas/TrackingModeB86Enum" - }, - "loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1, - "nullable": true - }, - "max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0, - "nullable": true - }, - "total_quantity": { - "type": "integer", - "readOnly": true - }, - "available_quantity": { - "type": "integer", - "readOnly": true - }, - "reserved_quantity": { - "type": "integer", - "readOnly": true - }, - "issued_quantity": { - "type": "integer", - "readOnly": true - }, - "damaged_quantity": { - "type": "integer", - "readOnly": true - }, - "lost_quantity": { - "type": "integer", - "readOnly": true - }, - "needs_fix_quantity": { - "type": "integer", - "readOnly": true - }, - "is_public": { - "type": "boolean" + "website": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 200 + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "public_self_checkout_enabled": { + "public_profile_enabled": { "type": "boolean" - }, - "show_public_count": { + } + } + }, + "PatchedPlatformBackupSettings": { + "type": "object", + "properties": { + "automatic_backups_enabled": { "type": "boolean" }, - "public_availability_mode": { - "$ref": "#/components/schemas/PublicAvailabilityModeFc2Enum" + "retention_days": { + "type": "integer", + "maximum": 3650, + "minimum": 1 }, - "storage_location": { + "last_scheduled_at": { "type": "string", - "maxLength": 200 + "format": "date-time", + "readOnly": true, + "nullable": true }, - "is_archived": { - "type": "boolean" + "last_success_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true }, - "created_at": { + "last_error": { "type": "string", - "format": "date-time", "readOnly": true }, "updated_at": { @@ -91735,58 +113950,46 @@ } } }, - "PatchedMachine": { + "PatchedPlatformEmailSettings": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "makerspace": { - "type": "integer", - "readOnly": true - }, - "machine_type": { - "allOf": [ - { - "$ref": "#/components/schemas/MachineType" - } - ], - "readOnly": true + "smtp_host": { + "type": "string", + "maxLength": 200 }, - "machine_type_id": { + "smtp_port": { "type": "integer", - "writeOnly": true + "maximum": 2147483647, + "minimum": 0 }, - "name": { + "smtp_username": { "type": "string", "maxLength": 200 }, - "location": { + "smtp_password": { "type": "string", - "maxLength": 200 - }, - "notes": { - "type": "string" + "writeOnly": true }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/MachineStatusEnum" - } - ], + "smtp_password_set": { + "type": "boolean", "readOnly": true }, - "firmware_version": { - "type": "string", - "maxLength": 100 + "smtp_use_tls": { + "type": "boolean" }, - "camera_feed_url": { + "smtp_use_ssl": { + "type": "boolean" + }, + "from_email": { "oneOf": [ { "type": "string", - "format": "uri", - "maxLength": 200 + "format": "email", + "maxLength": 254 }, { "type": "string", @@ -91794,694 +113997,549 @@ } ] }, - "status_since": { + "updated_at": { "type": "string", "format": "date-time", "readOnly": true - }, - "public_fields": {}, - "type_payload": {}, - "image_url": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "warranty_status": { - "type": "string", - "readOnly": true - }, - "is_public": { - "type": "boolean", - "readOnly": true - }, - "is_active": { - "type": "boolean", - "readOnly": true - }, - "usage_hours": { - "type": "number", - "format": "double", - "readOnly": true - }, - "can_operate": { - "type": "boolean", - "readOnly": true - }, - "can_edit": { + } + } + }, + "PatchedPlatformSocialAuthSettings": { + "type": "object", + "properties": { + "google_enabled": { "type": "boolean", "readOnly": true }, - "can_delegate": { + "apple_enabled": { "type": "boolean", "readOnly": true }, - "can_retire": { + "github_enabled": { "type": "boolean", "readOnly": true }, - "can_unretire": { - "type": "boolean", - "readOnly": true + "google_web_client_id": { + "type": "string", + "maxLength": 255 }, - "can_manage": { - "type": "boolean", - "readOnly": true + "google_ios_client_id": { + "type": "string", + "maxLength": 255 }, - "created_at": { + "google_android_client_id": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 255 }, - "updated_at": { + "apple_service_id": { "type": "string", - "format": "date-time", - "readOnly": true - } - } - }, - "PatchedMachinePublicity": { - "type": "object", - "properties": { - "is_public": { - "type": "boolean" - } - } - }, - "PatchedMachineTypeUpdate": { - "type": "object", - "properties": { - "name": { + "maxLength": 255 + }, + "apple_native_app_ids": {}, + "apple_team_id": { "type": "string", - "maxLength": 200 + "maxLength": 64 }, - "icon": { + "apple_key_id": { "type": "string", - "maxLength": 50 + "maxLength": 64 }, - "capability_config": {} - } - }, - "PatchedMaintenanceScheduleWrite": { - "type": "object", - "properties": { - "description": { - "type": "string" + "apple_private_key": { + "type": "string", + "writeOnly": true }, - "interval_days": { - "type": "integer", - "minimum": 1 + "apple_private_key_set": { + "type": "boolean", + "readOnly": true }, - "next_due": { + "github_client_id": { "type": "string", - "format": "date" - }, - "trigger": { - "$ref": "#/components/schemas/MaintenanceScheduleWriteTriggerEnum" + "maxLength": 255 }, - "usage_hours": { + "github_client_secret": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "nullable": true + "writeOnly": true, + "maxLength": 4096 }, - "job_count": { - "type": "integer", - "minimum": 1, - "nullable": true + "github_client_secret_set": { + "type": "boolean", + "readOnly": true }, - "anchor": { - "$ref": "#/components/schemas/AnchorEnum" + "clear_github_client_secret": { + "type": "boolean", + "writeOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } } }, - "PatchedMakerspace": { + "PatchedPlatformStripeConnectSettings": { "type": "object", "properties": { - "creation_setup": { - "allOf": [ - { - "$ref": "#/components/schemas/MakerspaceCreationSetup" - } - ], - "writeOnly": true - }, - "creation_invitation": { - "allOf": [ - { - "$ref": "#/components/schemas/CreationInviteDelivery" - } - ], - "readOnly": true, - "nullable": true - }, - "install_profile_id": { - "type": "integer", - "minimum": 1, - "writeOnly": true - }, "id": { "type": "integer", "readOnly": true }, - "name": { - "type": "string", - "maxLength": 200 - }, - "public_code": { + "stripe_publishable_key": { "type": "string", - "pattern": "^[A-Z0-9]{4}$", - "maxLength": 4 + "writeOnly": true, + "maxLength": 255 }, - "slug": { - "type": "string", - "maxLength": 50, - "pattern": "^[-a-zA-Z0-9_]+$" + "stripe_publishable_key_set": { + "type": "boolean", + "readOnly": true }, - "location": { + "stripe_secret_key": { "type": "string", - "maxLength": 200 + "writeOnly": true }, - "timezone": { - "type": "string", + "stripe_secret_key_set": { + "type": "boolean", "readOnly": true }, - "country": { + "stripe_webhook_secret": { "type": "string", - "readOnly": true + "writeOnly": true }, - "opening_hours": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "required": [ - "weekly", - "exceptions" - ], - "additionalProperties": false, - "properties": { - "weekly": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "additionalProperties": false, - "required": [ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun" - ], - "properties": { - "mon": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "tue": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "wed": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "thu": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "fri": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sat": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sun": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - } - } - } - ] - }, - "exceptions": { - "type": "object", - "maxProperties": 366, - "additionalProperties": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." - } - } - } - ], - "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days.", + "stripe_webhook_secret_set": { + "type": "boolean", "readOnly": true }, - "map_url": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "geofence_latitude": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,3}(?:\\.\\d{0,6})?$", - "nullable": true - }, - "geofence_longitude": { + "stripe_connect_client_id": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,3}(?:\\.\\d{0,6})?$", - "nullable": true + "maxLength": 255 }, - "geofence_radius_m": { + "application_fee_bps": { "type": "integer", - "maximum": 2147483647, - "minimum": 1 + "maximum": 10000, + "minimum": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + } + }, + "PatchedPreferenceWrite": { + "type": "object", + "properties": { + "feature": { + "$ref": "#/components/schemas/Feature9d8Enum" }, - "geofence_enabled": { - "type": "boolean" + "channel": { + "$ref": "#/components/schemas/ChannelB6fEnum" }, - "public_inventory_enabled": { + "enabled": { "type": "boolean" }, - "public_site_enabled": { + "daily_digest": { + "type": "boolean" + } + } + }, + "PatchedPrinterPoolVisibility": { + "type": "object", + "properties": { + "is_public": { "type": "boolean" + } + } + }, + "PatchedRegistrationAnswers": { + "type": "object", + "properties": { + "custom_answers": { + "nullable": true + } + } + }, + "PatchedReportSchedule": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true }, - "public_landing_page": { - "$ref": "#/components/schemas/PublicLandingPage6a0Enum" + "makerspace": { + "type": "integer", + "readOnly": true }, - "public_stats_enabled": { - "type": "boolean" + "report_key": { + "$ref": "#/components/schemas/ReportKeyEnum" }, - "public_stats_show_holder_names": { - "type": "boolean" + "filters": { + "$ref": "#/components/schemas/ReportScheduleFilters" }, - "public_print_status_lookup_policy": { - "$ref": "#/components/schemas/PublicPrintStatusLookupPolicyEnum" + "grain": { + "type": "string", + "maxLength": 8 }, - "membership_policy": { - "$ref": "#/components/schemas/MembershipPolicyEnum" + "format": { + "$ref": "#/components/schemas/ReportScheduleFormatEnum" }, - "membership_dues_amount": { + "cadence": { + "$ref": "#/components/schemas/CadenceEnum" + }, + "next_run_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "date-time" }, - "referrals_enabled": { - "type": "boolean" + "last_run_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true }, - "lapsed_members_cannot_borrow": { + "is_active": { "type": "boolean" }, - "anonymous_requests_enabled": { - "type": "boolean" + "destination": { + "type": "integer", + "nullable": true }, - "request_access": { + "recipient_emails": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "maxItems": 10 + }, + "created_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "filament_low_stock_threshold_grams": { + "updated_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$" - }, - "superadmin_access_enabled": { - "type": "boolean" + "format": "date-time", + "readOnly": true }, - "archive_custody_state": { + "last_delivery": { "allOf": [ { - "$ref": "#/components/schemas/ArchiveCustodyStateEnum" + "$ref": "#/components/schemas/ReportDelivery" } ], "nullable": true, "readOnly": true }, - "staff_notifications_enabled": { - "type": "boolean" - }, - "booking_requester_notifications_enabled": { - "type": "boolean" + "condition": { + "$ref": "#/components/schemas/ReportCondition" }, - "event_default_registration_audience": { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + "recipient_roles": { + "type": "array", + "items": { + "type": "string", + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "maxItems": 20 }, - "event_reminder_lead_hours": { + "recipient_actions": { "type": "array", "items": { - "type": "integer", - "maximum": 8760, - "minimum": 1 + "$ref": "#/components/schemas/RecipientActionsEnum" }, - "maxItems": 100 + "maxItems": 20 + } + } + }, + "PatchedReturnPolicy": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true }, - "booking_reminder_lead_hours": { + "default_loan_days": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "loan_renewal_mode": { + "$ref": "#/components/schemas/LoanRenewalModeEnum" + }, + "loan_max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0 + }, + "loan_renewal_days": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "loan_max_active": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "loan_max_items": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "loan_reminder_offsets_hours": { "type": "array", "items": { "type": "integer", - "maximum": 8760, - "minimum": 1 + "minimum": -8760, + "maximum": 8760 }, - "maxItems": 100 + "maxItems": 100, + "uniqueItems": true, + "description": "Signed whole hours from due time: negative before, zero due, positive overdue." }, - "logo_key": { - "type": "string", - "readOnly": true + "loan_escalate_overdue_days": { + "type": "integer", + "maximum": 365, + "minimum": 0 + } + } + }, + "PatchedRoleSetInput": { + "type": "object", + "properties": { + "role_id": { + "type": "integer", + "description": "Single-role set; omit role_ids." }, - "logo_url": { - "type": "string", - "format": "uri", - "nullable": true, + "role_ids": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "Complete role set. Space Manager and Member must each be assigned alone." + } + } + }, + "PatchedSavedReportView": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "cover_image_key": { - "type": "string", + "makerspace": { + "type": "integer", "readOnly": true }, - "cover_image_url": { - "type": "string", - "format": "uri", - "nullable": true, + "owner": { + "type": "integer", "readOnly": true }, - "frontend_domain": { + "name": { "type": "string", - "nullable": true, - "maxLength": 255 + "maxLength": 80 }, - "frontend_domain_status": { - "allOf": [ - { - "$ref": "#/components/schemas/FrontendDomainStatusEnum" - } - ], - "readOnly": true + "state": { + "$ref": "#/components/schemas/ReportViewState" }, - "domain_verified_at": { + "shared": { + "type": "boolean" + }, + "created_at": { "type": "string", "format": "date-time", - "readOnly": true, - "nullable": true + "readOnly": true }, - "domain_verification_token": { + "updated_at": { "type": "string", + "format": "date-time", + "readOnly": true + } + } + }, + "PatchedSavedView": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "domain_verification_record": { - "type": "object", - "nullable": true, - "properties": { - "host": { - "type": "string" - }, - "type": { - "type": "string" - }, - "value": { - "type": "string" - } - }, + "makerspace": { + "type": "integer", "readOnly": true }, - "platform_hosting": { - "type": "boolean", + "owner": { + "type": "integer", "readOnly": true }, - "is_platform_subdomain": { - "type": "boolean", + "owner_username": { + "type": "string", "readOnly": true }, - "hidden_from_central_directory": { + "name": { + "type": "string", + "maxLength": 80 + }, + "entity": { + "$ref": "#/components/schemas/SavedViewEntityEnum" + }, + "state": { + "type": "object", + "additionalProperties": { + "type": "string", + "maxLength": 250 + } + }, + "shared": { "type": "boolean" }, - "public_api_key": { + "created_at": { "type": "string", + "format": "date-time", "readOnly": true }, - "cors_allowed_origins": { + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + } + }, + "PatchedSchedule": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "enabled_modules": { + "name": { + "type": "string", + "maxLength": 200 + }, + "container": { + "type": "integer", + "nullable": true + }, + "interval_days": { + "type": "integer", + "minimum": 1 + }, + "next_due": { + "type": "string", + "format": "date" + }, + "blind": { + "type": "boolean" + }, + "is_active": { + "type": "boolean" + } + } + }, + "PatchedStaffGrantPermissions": { + "type": "object", + "properties": { + "permissions": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/Permissions5d1Enum" }, - "readOnly": true + "maxItems": 5, + "minItems": 1 + } + } + }, + "PatchedTimingPolicy": { + "type": "object", + "properties": { + "quiet_start": { + "type": "string", + "format": "time" }, - "unavailable_apps": { + "quiet_end": { + "type": "string", + "format": "time" + }, + "channels": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/ChannelsEnum" }, - "readOnly": true + "maxItems": 9 }, - "resource_limit_overrides": {}, - "enabled_features": {}, - "theme_config": { - "type": "object", - "additionalProperties": false, - "description": "Public/member secondary colours. Foreground, hover/pressed states and light/dark link ink are derived automatically. Rejects palettes below 4.5:1 text or 3:1 component contrast in either mode (including 80% counter text and tinted surfaces). Blank colours use the product palette. Staff chrome and status colours stay fixed.", - "properties": { - "mode": { - "type": "string", - "enum": [ - "system", - "light", - "dark" - ] - }, - "primary_color": { - "type": "string", - "pattern": "^$|^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" - }, - "accent_color": { + "digest_at": { + "type": "string", + "format": "time" + }, + "daily_digest_default": { + "type": "boolean" + }, + "bounce_secret": { + "writeOnly": true, + "oneOf": [ + { "type": "string", - "pattern": "^$|^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + "maxLength": 200, + "minLength": 32 }, - "logo_url": { + { "type": "string", - "maxLength": 2048 + "maxLength": 0 } - } + ] }, - "branding_config": { + "bounce_configured": { + "type": "boolean", + "readOnly": true + } + } + }, + "PatchedToBuyItem": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "public_display_name": { - "type": "string", - "writeOnly": true, - "maxLength": 200 - }, - "telegram_group_chat_id": { - "type": "string", - "maxLength": 64 - }, - "telegram_bot_token": { - "type": "string", - "writeOnly": true - }, - "telegram_bot_token_set": { - "type": "boolean", + "makerspace": { + "type": "integer", "readOnly": true }, - "smtp_host": { - "type": "string", - "maxLength": 200 + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/ToBuyItemKindEnum" + } + ], + "readOnly": true }, - "smtp_port": { + "machine_type": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "nullable": true }, - "smtp_username": { + "machine_type_name": { "type": "string", - "maxLength": 200 + "readOnly": true, + "nullable": true }, - "smtp_password": { + "name": { "type": "string", - "writeOnly": true - }, - "smtp_password_set": { - "type": "boolean", - "readOnly": true - }, - "smtp_use_tls": { - "type": "boolean" + "maxLength": 200 }, - "smtp_use_ssl": { - "type": "boolean" + "quantity": { + "type": "integer", + "minimum": 1, + "default": 1 }, - "smtp_from_email": { + "link": { "oneOf": [ { "type": "string", - "format": "email", - "maxLength": 254 + "format": "uri", + "maxLength": 200 }, { "type": "string", @@ -92489,39 +114547,92 @@ } ] }, - "slack_webhook_url": { + "status": { + "$ref": "#/components/schemas/ToBuyItemStatusEnum" + }, + "estimated_unit_cost": { "type": "string", - "writeOnly": true, - "maxLength": 2048 + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "vendor_name": { + "type": "string", + "maxLength": 200 + }, + "actual_unit_cost": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "purchaser": { + "type": "integer", + "readOnly": true + }, + "purchaser_username": { + "type": "string", + "readOnly": true + }, + "ordered_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "received_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "moved_to_inventory_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "resulting_product": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "slack_webhook_url_set": { - "type": "boolean", - "readOnly": true + "resulting_pool": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "mattermost_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 + "resulting_machine": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "mattermost_webhook_url_set": { - "type": "boolean", - "readOnly": true + "source_pool": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "discord_webhook_url": { - "type": "string", - "writeOnly": true, - "maxLength": 2048 + "source_product": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "discord_webhook_url_set": { - "type": "boolean", + "receipts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToBuyReceipt" + }, "readOnly": true }, - "default_loan_days": { + "created_by": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "readOnly": true, + "nullable": true + }, + "created_by_username": { + "type": "string", + "readOnly": true }, - "presence_preset_minutes": {}, "created_at": { "type": "string", "format": "date-time", @@ -92534,421 +114645,402 @@ } } }, - "PatchedMakerspacePaymentSettings": { + "PatchedWhatsAppBinding": { "type": "object", "properties": { - "default_currency": { + "id": { + "type": "integer", + "readOnly": true + }, + "feature": { "type": "string", - "maxLength": 3 + "maxLength": 32 }, - "stripe_publishable_key": { + "event": { "type": "string", - "writeOnly": true, - "maxLength": 255 + "maxLength": 64 }, - "stripe_publishable_key_set": { - "type": "boolean", - "readOnly": true + "audience": { + "$ref": "#/components/schemas/Audience356Enum" }, - "stripe_secret_key": { + "business_account_id": { "type": "string", - "writeOnly": true - }, - "stripe_secret_key_set": { - "type": "boolean", "readOnly": true }, - "stripe_webhook_secret": { + "template_name": { "type": "string", - "writeOnly": true - }, - "stripe_webhook_secret_set": { - "type": "boolean", - "readOnly": true + "maxLength": 512 }, - "connect_account_id": { + "language": { "type": "string", - "readOnly": true, - "nullable": true + "maxLength": 16 }, - "connect_status": { - "allOf": [ - { - "$ref": "#/components/schemas/ConnectStatusEnum" - } - ], - "readOnly": true + "parameters": {}, + "is_active": { + "type": "boolean" }, - "connect_charges_enabled": { + "needs_review": { "type": "boolean", "readOnly": true }, - "connect_payouts_enabled": { - "type": "boolean", - "readOnly": true + "components": { + "writeOnly": true + } + } + }, + "PaymentAgeingRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "connect_status_updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "currency": { + "type": "string" }, - "effective_mode": { - "type": "string", - "readOnly": true + "age_bucket": { + "$ref": "#/components/schemas/AgeBucketEnum" }, - "loan_deposit_mode": { - "$ref": "#/components/schemas/LoanDepositModeEnum" + "payment_count": { + "type": "integer" }, - "loan_deposit_amount": { + "outstanding_amount": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$" + } + }, + "required": [ + "age_bucket", + "currency", + "outstanding_amount", + "payment_count" + ] + }, + "PaymentBulkAction": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1 + } + } + }, + "required": [ + "ids" + ] + }, + "PaymentBulkOffline": { + "type": "object", + "properties": { + "ids": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1 + } }, - "loan_late_fee_per_day": { + "settlement": { + "$ref": "#/components/schemas/ManualSettlement" + } + }, + "required": [ + "ids", + "settlement" + ] + }, + "PaymentCashBook": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentCashBookRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "PaymentCashBookRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "method": { + "type": "string" + }, + "collected_amount": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "loan_late_fee_cap": { + "refunded_amount": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "loan_grace_days": { - "type": "integer", - "maximum": 32767, - "minimum": 0 + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "loan_deposit_blocks_issue": { - "type": "boolean" + "net_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" } - } + }, + "required": [ + "collected_amount", + "currency", + "method", + "net_amount", + "refunded_amount" + ] }, - "PatchedMakerspaceProfile": { + "PaymentDocument": { "type": "object", "properties": { - "timezone": { + "id": { + "type": "integer", + "readOnly": true + }, + "payment": { + "type": "integer" + }, + "number": { "type": "string", - "maxLength": 100 + "maxLength": 80 }, - "country": { + "kind": { + "$ref": "#/components/schemas/PaymentDocumentKindEnum" + }, + "issued_at": { "type": "string", - "maxLength": 2 + "format": "date-time", + "readOnly": true }, - "opening_hours": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "required": [ - "weekly", - "exceptions" - ], - "additionalProperties": false, - "properties": { - "weekly": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "additionalProperties": false, - "required": [ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun" - ], - "properties": { - "mon": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "tue": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "wed": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "thu": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "fri": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sat": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sun": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - } - } - } - ] - }, - "exceptions": { - "type": "object", - "maxProperties": 366, - "additionalProperties": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." - } - } - } - ], - "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." + "money_at": { + "type": "string", + "format": "date-time" + }, + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "currency": { + "type": "string", + "maxLength": 3 + } + }, + "required": [ + "amount", + "currency", + "id", + "issued_at", + "kind", + "money_at", + "number", + "payment" + ] + }, + "PaymentDocumentKindEnum": { + "enum": [ + "receipt", + "credit_note" + ], + "type": "string", + "description": "* `receipt` - Receipt\n* `credit_note` - Credit note" + }, + "PaymentFilterError": { + "oneOf": [ + { + "$ref": "#/components/schemas/HardwareRequestError" + }, + { + "$ref": "#/components/schemas/PaymentFilterValidationError" + } + ] + }, + "PaymentFilterValidationError": { + "type": "object", + "properties": { + "due_state": { + "type": "array", + "items": { + "type": "string" + } }, - "default_currency": { - "type": "string", - "maxLength": 3 + "status": { + "type": "array", + "items": { + "type": "string" + } }, - "timezone_choices": { + "subject_type": { "type": "array", "items": { "type": "string" - }, - "readOnly": true + } }, - "country_choices": { + "for_recording": { "type": "array", "items": { "type": "string" - }, - "readOnly": true + } } } }, - "PatchedMaterial": { + "PaymentListResponse": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "machine_type": { + "count": { "type": "integer" }, - "name": { + "next": { "type": "string", - "maxLength": 100 + "format": "uri", + "nullable": true }, - "colours": {}, - "is_active": { - "type": "boolean" + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentReconciliation" + } } - } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] }, - "PatchedMemberCardOwnUpdate": { + "PaymentOffline": { "type": "object", "properties": { - "printed_name": { - "type": "string", - "maxLength": 200 + "settlement": { + "$ref": "#/components/schemas/ManualSettlement" } - } + }, + "required": [ + "settlement" + ] }, - "PatchedMembershipCapabilities": { + "PaymentOfflineRefund": { "type": "object", "properties": { - "can_refer": { - "type": "boolean" + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "can_verify": { - "type": "boolean" + "reason": { + "type": "string", + "default": "", + "maxLength": 255 + }, + "receipt": { + "$ref": "#/components/schemas/ManualRefundReceipt" } - } + }, + "required": [ + "amount", + "receipt" + ] }, - "PatchedMembershipPlan": { + "PaymentReconciliation": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "name": { + "subject_type": { + "allOf": [ + { + "$ref": "#/components/schemas/SubjectTypeEnum" + } + ], + "readOnly": true + }, + "subject_id": { + "type": "integer", + "readOnly": true + }, + "subject_label": { "type": "string", - "maxLength": 120 + "readOnly": true }, - "interval": { - "$ref": "#/components/schemas/IntervalEnum" + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/Status66aEnum" + } + ], + "readOnly": true }, - "custom_days": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, + "due_at": { + "type": "string", + "format": "date-time", + "readOnly": true, "nullable": true }, + "due_date": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "due_state": { + "type": "string", + "readOnly": true + }, "amount": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true }, "currency": { "type": "string", - "maxLength": 3 + "readOnly": true }, - "is_active": { - "type": "boolean" + "refunded_amount": { + "type": "number", + "format": "double", + "readOnly": true + }, + "refunds": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Refund" + }, + "readOnly": true + }, + "settlement": { + "allOf": [ + { + "$ref": "#/components/schemas/StaffSettlement" + } + ], + "nullable": true, + "readOnly": true }, "created_at": { "type": "string", @@ -92959,111 +115051,465 @@ "type": "string", "format": "date-time", "readOnly": true + } + }, + "required": [ + "amount", + "created_at", + "currency", + "due_at", + "due_date", + "due_state", + "id", + "refunded_amount", + "refunds", + "settlement", + "status", + "subject_id", + "subject_label", + "subject_type", + "updated_at" + ] + }, + "PaymentReconciliationReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "loan_max_active": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentReconciliationReportRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "PaymentReconciliationReportRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "loan_max_items": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true + "currency": { + "type": "string" }, - "auto_renew": { - "type": "boolean" + "subject_type": { + "type": "string" }, - "renewal_open_days": { - "type": "integer", - "maximum": 365, - "minimum": 0 + "status": { + "type": "string" }, - "renewal_grace_days": { - "type": "integer", - "maximum": 365, - "minimum": 0 + "payment_count": { + "type": "integer" }, - "renewal_retries": { - "type": "integer", - "maximum": 3, - "minimum": 0 + "amount_total": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "reminder_days_before": {}, - "reminder_days_after": {} - } + "outstanding_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + }, + "settlement_method": { + "type": "string" + }, + "refunded_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + } + }, + "required": [ + "amount_total", + "currency", + "outstanding_amount", + "payment_count", + "refunded_amount", + "settlement_method", + "status", + "subject_type" + ] + }, + "PaymentRefundAmend": { + "type": "object", + "properties": { + "receipt": { + "$ref": "#/components/schemas/ManualRefundReceipt" + } + }, + "required": [ + "receipt" + ] + }, + "PaymentRefundRequest": { + "type": "object", + "properties": { + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "reason": { + "type": "string", + "default": "", + "maxLength": 255 + } + }, + "required": [ + "amount" + ] + }, + "PaymentSettingsError": { + "type": "object", + "properties": { + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "PaymentTimingEnum": { + "enum": [ + "after_activation", + "before_activation" + ], + "type": "string", + "description": "* `after_activation` - Activate then charge\n* `before_activation` - Pay before activation" + }, + "PaymentsAgeingReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PaymentAgeingRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "PaymentsLedgerReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/LedgerLine" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "PendingClosure": { + "type": "object", + "properties": { + "digest": { + "type": "string", + "maxLength": 64 + }, + "identities": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ClosureIdentity" + } + } + }, + "required": [ + "digest", + "identities" + ] + }, + "Permissions43eEnum": { + "enum": [ + "edit_details", + "manage_reservations", + "check_in", + "publish_page" + ], + "type": "string", + "description": "* `edit_details` - edit_details\n* `manage_reservations` - manage_reservations\n* `check_in` - check_in\n* `publish_page` - publish_page" + }, + "Permissions5d1Enum": { + "enum": [ + "edit_details", + "manage_registrations", + "manage_invitations", + "check_in", + "publish_schedule" + ], + "type": "string", + "description": "* `edit_details` - edit_details\n* `manage_registrations` - manage_registrations\n* `manage_invitations` - manage_invitations\n* `check_in` - check_in\n* `publish_schedule` - publish_schedule" + }, + "PhaseEnum": { + "enum": [ + "issue", + "return" + ], + "type": "string", + "description": "* `issue` - issue\n* `return` - return" + }, + "PhoneConfirm": { + "type": "object", + "properties": { + "phone": { + "type": "string", + "maxLength": 32 + }, + "code": { + "type": "string", + "maxLength": 6, + "minLength": 6 + } + }, + "required": [ + "code", + "phone" + ] + }, + "PhoneLoginConfirm": { + "type": "object", + "properties": { + "phone": { + "type": "string", + "maxLength": 32 + }, + "delivery": { + "allOf": [ + { + "$ref": "#/components/schemas/DeliveryEnum" + } + ], + "default": "web" + }, + "client_platform": { + "allOf": [ + { + "$ref": "#/components/schemas/ClientPlatformEnum" + } + ], + "default": "web" + }, + "challenge": { + "type": "string", + "writeOnly": true, + "maxLength": 512 + }, + "code": { + "type": "string", + "maxLength": 6, + "minLength": 6 + }, + "attestation": { + "allOf": [ + { + "$ref": "#/components/schemas/DeviceAttestationPayload" + } + ], + "writeOnly": true + } + }, + "required": [ + "code", + "phone" + ] + }, + "PhoneLoginResult": { + "oneOf": [ + { + "$ref": "#/components/schemas/MFACompleted" + }, + { + "$ref": "#/components/schemas/MFAChallengeResponse" + }, + { + "$ref": "#/components/schemas/DeviceTokenResponse" + } + ] + }, + "PhoneLoginStart": { + "type": "object", + "properties": { + "phone": { + "type": "string", + "maxLength": 32 + }, + "delivery": { + "allOf": [ + { + "$ref": "#/components/schemas/DeliveryEnum" + } + ], + "default": "web" + }, + "client_platform": { + "allOf": [ + { + "$ref": "#/components/schemas/ClientPlatformEnum" + } + ], + "default": "web" + }, + "challenge": { + "type": "string", + "writeOnly": true, + "maxLength": 512 + } + }, + "required": [ + "phone" + ] + }, + "PhoneStart": { + "type": "object", + "properties": { + "phone": { + "type": "string", + "maxLength": 32 + } + }, + "required": [ + "phone" + ] }, - "PatchedMembershipRoleAssign": { + "PhoneStartResponse": { "type": "object", "properties": { - "role_id": { - "type": "integer" + "detail": { + "type": "string" } - } + }, + "required": [ + "detail" + ] }, - "PatchedNotificationRecipientsPatch": { + "PhoneStatus": { "type": "object", "properties": { - "recipients": { - "type": "array", - "items": { - "$ref": "#/components/schemas/NotificationRecipientUpdate" - } + "phone_e164": { + "type": "string" + }, + "verified": { + "type": "boolean" } - } + }, + "required": [ + "phone_e164", + "verified" + ] }, - "PatchedNotificationRulesPatch": { + "PlatformAnalyticsReport": { "type": "object", "properties": { - "changes": { + "report_key": { + "type": "string" + }, + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationRuleChange" + "type": "array", + "items": {} } }, - "preferences": { + "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/NotificationPreferenceChange" + "type": "object", + "additionalProperties": {} } + }, + "meta": { + "type": "object", + "additionalProperties": {} + }, + "chart_spec": { + "allOf": [ + { + "$ref": "#/components/schemas/ReportChartSpec" + } + ], + "description": "Canonical chart fields, units, and partitions from the report definition." } - } + }, + "required": [ + "chart_spec", + "rows" + ] }, - "PatchedOrganizationProfileUpdate": { + "PlatformBackupHealth": { "type": "object", "properties": { - "name": { - "type": "string", - "maxLength": 200 + "automatic_enabled": { + "type": "boolean" }, - "slug": { + "last_success_at": { "type": "string", - "maxLength": 50, - "pattern": "^[-a-zA-Z0-9_]+$" + "format": "date-time", + "nullable": true }, - "description": { + "status": { "type": "string" }, - "website": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "started_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "public_profile_enabled": { + "finished_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "has_error": { "type": "boolean" + }, + "cadence_minutes": { + "type": "integer" } - } + }, + "required": [ + "automatic_enabled", + "cadence_minutes", + "finished_at", + "has_error", + "last_success_at", + "started_at", + "status" + ] }, - "PatchedPlatformBackupSettings": { + "PlatformBackupSettings": { "type": "object", "properties": { "automatic_backups_enabled": { @@ -93095,9 +115541,70 @@ "format": "date-time", "readOnly": true } - } + }, + "required": [ + "last_error", + "last_scheduled_at", + "last_success_at", + "updated_at" + ] }, - "PatchedPlatformEmailSettings": { + "PlatformDashboard": { + "type": "object", + "properties": { + "checked_at": { + "type": "string", + "format": "date-time" + }, + "tenants_by_state": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "usage_totals": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + }, + "tenants": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlatformTenantUsage" + } + }, + "backup": { + "$ref": "#/components/schemas/PlatformBackupHealth" + }, + "offsite": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlatformOffsiteHealth" + } + }, + "scheduler": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlatformTaskHealth" + } + }, + "failed_deliveries": { + "$ref": "#/components/schemas/FailedDeliveries" + } + }, + "required": [ + "backup", + "checked_at", + "failed_deliveries", + "offsite", + "scheduler", + "tenants", + "tenants_by_state", + "usage_totals" + ] + }, + "PlatformEmailSettings": { "type": "object", "properties": { "id": { @@ -93149,9 +115656,240 @@ "format": "date-time", "readOnly": true } - } + }, + "required": [ + "id", + "smtp_password_set", + "updated_at" + ] }, - "PatchedPlatformSocialAuthSettings": { + "PlatformEnum": { + "enum": [ + "apple", + "android" + ], + "type": "string", + "description": "* `apple` - Apple\n* `android` - Android" + }, + "PlatformOffsiteHealth": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "state": { + "type": "string" + }, + "verified_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "has_error": { + "type": "boolean" + } + }, + "required": [ + "has_error", + "name", + "state", + "verified_at" + ] + }, + "PlatformProblem": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "status": { + "$ref": "#/components/schemas/Status00eEnum" + }, + "message": { + "type": "string" + }, + "fix_url": { + "type": "string" + }, + "makerspace_id": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "fix_url", + "key", + "makerspace_id", + "message", + "status" + ] + }, + "PlatformProblems": { + "type": "object", + "properties": { + "checked_at": { + "type": "string", + "format": "date-time" + }, + "status": { + "$ref": "#/components/schemas/Status00eEnum" + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlatformProblem" + } + } + }, + "required": [ + "checked_at", + "results", + "status" + ] + }, + "PlatformReadiness": { + "type": "object", + "properties": { + "hint": { + "type": "string" + }, + "login_ready": { + "type": "boolean" + }, + "push_ready": { + "type": "boolean" + }, + "apple_revocation_ready": { + "type": "boolean" + }, + "associations_ready": { + "type": "boolean" + }, + "registrations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RegistrationReadiness" + } + } + }, + "required": [ + "apple_revocation_ready", + "associations_ready", + "hint", + "login_ready", + "push_ready", + "registrations" + ] + }, + "PlatformServicesReport": { + "type": "object", + "properties": { + "report_key": { + "type": "string" + }, + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlatformServicesRow" + } + }, + "meta": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "PlatformServicesRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer", + "nullable": true + }, + "period": { + "type": "string", + "format": "date", + "nullable": true + }, + "day": { + "type": "string", + "format": "date" + }, + "service": { + "type": "string" + }, + "provider_source": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "nullable": true + }, + "bucket": { + "type": "string" + }, + "count": { + "type": "integer", + "minimum": 0 + }, + "currency": { + "type": "string", + "nullable": true + }, + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "purpose": { + "nullable": true, + "description": "SMS OTP vs addressed notifications. Null for other services and hidden buckets.\n\n* `otp` - otp\n* `notifications` - notifications", + "oneOf": [ + { + "$ref": "#/components/schemas/PlatformServicesRowPurposeEnum" + }, + { + "$ref": "#/components/schemas/NullEnum" + } + ] + } + }, + "required": [ + "amount", + "bucket", + "count", + "currency", + "day", + "makerspace_id", + "period", + "provider_source", + "purpose", + "service", + "status" + ] + }, + "PlatformServicesRowPurposeEnum": { + "enum": [ + "otp", + "notifications" + ], + "type": "string", + "description": "* `otp` - otp\n* `notifications` - notifications" + }, + "PlatformSocialAuthSettings": { "type": "object", "properties": { "google_enabled": { @@ -93221,9 +115959,17 @@ "format": "date-time", "readOnly": true } - } + }, + "required": [ + "apple_enabled", + "apple_private_key_set", + "github_client_secret_set", + "github_enabled", + "google_enabled", + "updated_at" + ] }, - "PatchedPlatformStripeConnectSettings": { + "PlatformStripeConnectSettings": { "type": "object", "properties": { "id": { @@ -93255,2340 +116001,2747 @@ "type": "boolean", "readOnly": true }, - "stripe_connect_client_id": { - "type": "string", - "maxLength": 255 + "stripe_connect_client_id": { + "type": "string", + "maxLength": 255 + }, + "application_fee_bps": { + "type": "integer", + "maximum": 10000, + "minimum": 0 + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "id", + "stripe_publishable_key_set", + "stripe_secret_key_set", + "stripe_webhook_secret_set", + "updated_at" + ] + }, + "PlatformTaskHealth": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "task": { + "type": "string" + }, + "cadence_minutes": { + "type": "integer" + }, + "last_run_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/Status00eEnum" + } + }, + "required": [ + "cadence_minutes", + "last_run_at", + "name", + "status", + "task" + ] + }, + "PlatformTenantUsage": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "usage": { + "$ref": "#/components/schemas/MakerspaceUsage" + } + }, + "required": [ + "id", + "name", + "slug", + "usage" + ] + }, + "Policy": { + "type": "object", + "properties": { + "blind_default": { + "type": "boolean" + }, + "variance_threshold": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0, + "nullable": true + } + } + }, + "PositionedElement": { + "type": "object", + "properties": { + "field": { + "type": "string", + "maxLength": 80 + }, + "x_mm": { + "type": "number", + "format": "double", + "minimum": 0 + }, + "y_mm": { + "type": "number", + "format": "double", + "minimum": 0 + }, + "width_mm": { + "type": "number", + "format": "double", + "minimum": 0.5 + }, + "height_mm": { + "type": "number", + "format": "double", + "minimum": 0.5 + }, + "font": { + "$ref": "#/components/schemas/FontEnum" + }, + "font_size_pt": { + "type": "number", + "format": "double", + "maximum": 48, + "minimum": 6 + }, + "bold": { + "type": "boolean" }, - "application_fee_bps": { - "type": "integer", - "maximum": 10000, - "minimum": 0 + "align": { + "$ref": "#/components/schemas/AlignEnum" }, - "updated_at": { + "colour": { "type": "string", - "format": "date-time", - "readOnly": true + "pattern": "^#[0-9A-Fa-f]{6}$" } - } + }, + "required": [ + "field", + "height_mm", + "width_mm", + "x_mm", + "y_mm" + ] }, - "PatchedPreferenceWrite": { + "PreferenceCell": { "type": "object", "properties": { "feature": { "$ref": "#/components/schemas/Feature9d8Enum" }, "channel": { - "$ref": "#/components/schemas/Channel673Enum" + "$ref": "#/components/schemas/ChannelB6fEnum" }, "enabled": { "type": "boolean" }, "daily_digest": { "type": "boolean" + }, + "available": { + "type": "boolean" + }, + "mandatory_events": { + "type": "array", + "items": { + "type": "string" + } } - } + }, + "required": [ + "available", + "channel", + "enabled", + "feature", + "mandatory_events" + ] }, - "PatchedPrinterPoolVisibility": { + "PreferenceResponse": { "type": "object", "properties": { - "is_public": { + "cells": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PreferenceCell" + } + } + }, + "required": [ + "cells" + ] + }, + "PresenceCurrent": { + "type": "object", + "properties": { + "active": { "type": "boolean" + }, + "session": { + "allOf": [ + { + "$ref": "#/components/schemas/PresenceSession" + } + ], + "nullable": true } - } + }, + "required": [ + "active", + "session" + ] }, - "PatchedReportSchedule": { + "PresenceRoster": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "display_name": { + "type": "string" }, - "makerspace": { - "type": "integer", + "role_label": { + "type": "string", "readOnly": true }, - "report_key": { - "$ref": "#/components/schemas/ReportKeyEnum" - }, - "filters": { - "$ref": "#/components/schemas/ReportScheduleFilters" - }, - "grain": { + "started_at": { "type": "string", - "maxLength": 8 - }, - "format": { - "$ref": "#/components/schemas/ReportScheduleFormatEnum" - }, - "cadence": { - "$ref": "#/components/schemas/CadenceEnum" + "format": "date-time" }, - "next_run_at": { + "expires_at": { "type": "string", "format": "date-time" - }, - "last_run_at": { + } + }, + "required": [ + "display_name", + "expires_at", + "role_label", + "started_at" + ] + }, + "PresenceSession": { + "type": "object", + "properties": { + "started_at": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "is_active": { - "type": "boolean" - }, - "destination": { - "type": "integer", - "nullable": true - }, - "recipient_emails": { - "type": "array", - "items": { - "type": "string", - "format": "email" - }, - "maxItems": 10 - }, - "created_by": { - "type": "integer", - "readOnly": true, - "nullable": true + "format": "date-time" }, - "created_at": { + "expires_at": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "date-time" }, - "updated_at": { + "ended_at": { "type": "string", "format": "date-time", - "readOnly": true + "nullable": true }, - "last_delivery": { - "allOf": [ + "end_reason": { + "oneOf": [ { - "$ref": "#/components/schemas/ReportDelivery" + "$ref": "#/components/schemas/EndReasonEnum" + }, + { + "$ref": "#/components/schemas/BlankEnum" } - ], - "nullable": true, - "readOnly": true - }, - "condition": { - "$ref": "#/components/schemas/ReportCondition" - }, - "recipient_roles": { - "type": "array", - "items": { - "type": "string", - "maxLength": 80, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "maxItems": 20 + ] }, - "recipient_actions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RecipientActionsEnum" - }, - "maxItems": 20 + "active": { + "type": "boolean", + "readOnly": true } - } + }, + "required": [ + "active", + "expires_at", + "started_at" + ] }, - "PatchedReturnPolicy": { + "PresenceStart": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "default_loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1 - }, - "loan_renewal_mode": { - "$ref": "#/components/schemas/LoanRenewalModeEnum" - }, - "loan_max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0 - }, - "loan_renewal_days": { - "type": "integer", - "maximum": 365, - "minimum": 1 - }, - "loan_max_active": { + "duration_minutes": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "maximum": 480, + "minimum": 15 }, - "loan_max_items": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "latitude": { + "type": "number", + "format": "double", + "maximum": 90, + "minimum": -90 }, - "loan_reminder_offsets_hours": { - "type": "array", - "items": { - "type": "integer", - "minimum": -8760, - "maximum": 8760 - }, - "maxItems": 100, - "uniqueItems": true, - "description": "Signed whole hours from due time: negative before, zero due, positive overdue." + "longitude": { + "type": "number", + "format": "double", + "maximum": 180, + "minimum": -180 }, - "loan_escalate_overdue_days": { - "type": "integer", - "maximum": 365, + "accuracy": { + "type": "number", + "format": "double", "minimum": 0 } - } + }, + "required": [ + "duration_minutes" + ] }, - "PatchedRoleId": { - "type": "object", - "properties": { - "role_id": { - "type": "integer" - } - } + "PresetEnum": { + "enum": [ + "sheet", + "single" + ], + "type": "string", + "description": "* `sheet` - sheet\n* `single` - single" }, - "PatchedRoleWrite": { + "PrinterJobsReport": { "type": "object", "properties": { - "name": { - "type": "string", - "maxLength": 80 + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "granted_actions": { + "typed_rows": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/PrinterJobsRow" } } - } + }, + "required": [ + "rows", + "typed_rows" + ] }, - "PatchedSavedReportView": { + "PrinterJobsRow": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "makerspace_id": { + "type": "integer" }, - "makerspace": { - "type": "integer", - "readOnly": true + "period": { + "type": "string", + "format": "date", + "nullable": true }, - "owner": { + "machine_id": { "type": "integer", - "readOnly": true + "nullable": true }, - "name": { + "machine_name": { "type": "string", - "maxLength": 80 + "nullable": true }, - "state": { - "$ref": "#/components/schemas/ReportViewState" + "machine_type": { + "type": "string", + "nullable": true }, - "shared": { - "type": "boolean" + "state": { + "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "outcome": { + "type": "string" }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "job_count": { + "type": "integer" } - } + }, + "required": [ + "job_count", + "machine_id", + "machine_name", + "machine_type", + "outcome", + "period", + "state" + ] }, - "PatchedSavedView": { + "PrinterPool": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "makerspace": { + "machine_id": { "type": "integer", - "readOnly": true + "readOnly": true, + "nullable": true }, - "owner": { + "machine_type_id": { "type": "integer", - "readOnly": true + "readOnly": true, + "nullable": true }, - "owner_username": { + "material": { "type": "string", - "readOnly": true + "maxLength": 100 }, - "name": { + "color": { "type": "string", - "maxLength": 80 - }, - "entity": { - "$ref": "#/components/schemas/SavedViewEntityEnum" - }, - "state": { - "type": "object", - "additionalProperties": { - "type": "string", - "maxLength": 250 - } - }, - "shared": { - "type": "boolean" + "maxLength": 100 }, - "created_at": { + "color_hex": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 7 }, - "updated_at": { + "brand": { "type": "string", - "format": "date-time", - "readOnly": true - } - } - }, - "PatchedSchedule": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true + "maxLength": 100 }, - "name": { + "lot_code": { "type": "string", - "maxLength": 200 - }, - "container": { - "type": "integer", - "nullable": true + "maxLength": 100 }, - "interval_days": { - "type": "integer", - "minimum": 1 + "unit": { + "$ref": "#/components/schemas/PrinterPoolUnitEnum" }, - "next_due": { + "unit_cost": { "type": "string", - "format": "date" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,4})?$", + "nullable": true }, - "blind": { - "type": "boolean" + "cost_currency": { + "type": "string", + "maxLength": 3 }, - "is_active": { - "type": "boolean" - } - } - }, - "PatchedTimingPolicy": { - "type": "object", - "properties": { - "quiet_start": { + "cost_unit": { "type": "string", - "format": "time" + "maxLength": 16 }, - "quiet_end": { + "initial_grams": { "type": "string", - "format": "time" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "channels": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ChannelsEnum" - }, - "maxItems": 7 + "remaining_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true }, - "digest_at": { + "low_threshold_grams": { "type": "string", - "format": "time" + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "nullable": true }, - "daily_digest_default": { + "is_active": { + "type": "boolean", + "readOnly": true + }, + "is_public": { "type": "boolean" }, - "bounce_secret": { - "writeOnly": true, - "oneOf": [ - { - "type": "string", - "maxLength": 200, - "minLength": 32 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "opened_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "bounce_configured": { - "type": "boolean", + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", "readOnly": true } - } + }, + "required": [ + "created_at", + "id", + "initial_grams", + "is_active", + "machine_id", + "machine_type_id", + "material", + "remaining_grams", + "updated_at" + ] }, - "PatchedToBuyItem": { + "PrinterPoolCorrection": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer", - "readOnly": true - }, - "kind": { - "allOf": [ - { - "$ref": "#/components/schemas/ToBuyItemKindEnum" - } - ], - "readOnly": true + "quantity_delta": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "machine_type": { - "type": "integer", + "reason": { + "type": "string" + } + }, + "required": [ + "quantity_delta", + "reason" + ] + }, + "PrinterPoolCreate": { + "type": "object", + "properties": { + "unit_cost": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,4})?$", "nullable": true }, - "machine_type_name": { + "cost_currency": { "type": "string", - "readOnly": true, - "nullable": true + "maxLength": 3 }, - "name": { + "cost_unit": { "type": "string", - "maxLength": 200 + "maxLength": 16 }, - "quantity": { + "machine_id": { "type": "integer", "minimum": 1, - "default": 1 + "nullable": true }, - "link": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "machine_type_id": { + "type": "integer", + "minimum": 1, + "nullable": true }, - "status": { - "$ref": "#/components/schemas/ToBuyItemStatusEnum" + "is_public": { + "type": "boolean", + "default": true }, - "estimated_unit_cost": { + "material": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", - "nullable": true + "maxLength": 100 }, - "vendor_name": { + "color": { "type": "string", - "maxLength": 200 + "maxLength": 100 }, - "actual_unit_cost": { + "color_hex": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", - "nullable": true - }, - "purchaser": { - "type": "integer", - "readOnly": true + "maxLength": 7 }, - "purchaser_username": { + "brand": { "type": "string", - "readOnly": true + "maxLength": 100 }, - "ordered_at": { + "lot_code": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "maxLength": 100 }, - "received_at": { + "unit": { + "allOf": [ + { + "$ref": "#/components/schemas/PrinterPoolCreateUnitEnum" + } + ], + "default": "grams" + }, + "quantity": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "moved_to_inventory_at": { + "initial_grams": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "resulting_product": { - "type": "integer", - "readOnly": true, + "low_threshold_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "nullable": true + } + }, + "required": [ + "material" + ] + }, + "PrinterPoolCreateUnitEnum": { + "enum": [ + "grams", + "milliliters", + "millimeters", + "count" + ], + "type": "string", + "description": "* `grams` - grams\n* `milliliters` - milliliters\n* `millimeters` - millimeters\n* `count` - count" + }, + "PrinterPoolUnitEnum": { + "enum": [ + "grams", + "milliliters", + "millimeters", + "count" + ], + "type": "string", + "description": "* `grams` - Grams\n* `milliliters` - Milliliters\n* `millimeters` - Millimeters\n* `count` - Count" + }, + "PrinterServiceReport": { + "type": "object", + "properties": { + "printer_metrics": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PrinterServiceReportRow" + } + } + }, + "required": [ + "printer_metrics" + ] + }, + "PrinterServiceReportRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "resulting_pool": { - "type": "integer", - "readOnly": true, - "nullable": true + "machine_id": { + "type": "integer" }, - "resulting_machine": { - "type": "integer", - "readOnly": true, - "nullable": true + "machine_name": { + "type": "string" }, - "source_pool": { - "type": "integer", - "readOnly": true, - "nullable": true + "model": { + "type": "string" }, - "source_product": { - "type": "integer", - "readOnly": true, - "nullable": true + "completed_hours": { + "type": "number", + "format": "double" }, - "receipts": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ToBuyReceipt" - }, - "readOnly": true + "failed_partial_hours": { + "type": "number", + "format": "double" }, - "created_by": { - "type": "integer", - "readOnly": true, - "nullable": true + "manual_hours": { + "type": "number", + "format": "double" }, - "created_by_username": { + "consumed_grams": { "type": "string", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "created_at": { + "payment_due": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "updated_at": { + "payment_paid": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" } - } + }, + "required": [ + "completed_hours", + "consumed_grams", + "failed_partial_hours", + "machine_id", + "machine_name", + "manual_hours", + "model", + "payment_due", + "payment_paid" + ] }, - "PaymentBulkAction": { + "ProblemReportTriage": { "type": "object", "properties": { - "ids": { + "outcome": { + "$ref": "#/components/schemas/ProblemReportTriageOutcomeEnum" + }, + "resolutions": { "type": "array", "items": { - "type": "integer", - "minimum": 1 + "$ref": "#/components/schemas/ProblemReportTriageResolution" } + }, + "note": { + "type": "string", + "default": "" + }, + "evidence_id": { + "type": "integer", + "nullable": true } }, "required": [ - "ids" + "outcome" ] }, - "PaymentBulkOffline": { + "ProblemReportTriageOutcomeEnum": { + "enum": [ + "no_issue", + "damaged", + "missing", + "needs_fix" + ], + "type": "string", + "description": "* `no_issue` - no_issue\n* `damaged` - damaged\n* `missing` - missing\n* `needs_fix` - needs_fix" + }, + "ProblemReportTriageResolution": { "type": "object", "properties": { - "ids": { - "type": "array", - "items": { - "type": "integer", - "minimum": 1 - } + "item_id": { + "type": "integer" }, - "settlement": { - "$ref": "#/components/schemas/ManualSettlement" + "quantity": { + "type": "integer", + "minimum": 1 } }, "required": [ - "ids", - "settlement" + "item_id", + "quantity" ] }, - "PaymentCashBook": { + "ProblemReportTriageResponse": { "type": "object", "properties": { - "rows": { + "id": { + "type": "integer" + }, + "outcome": { + "type": "string" + }, + "resolved": { + "type": "boolean" + } + }, + "required": [ + "id", + "outcome", + "resolved" + ] + }, + "ProductQrHistory": { + "type": "object", + "properties": { + "product": { + "type": "integer" + }, + "scans": { "type": "array", "items": { - "type": "array", - "items": {} + "$ref": "#/components/schemas/QrHistoryItem" } + } + }, + "required": [ + "product", + "scans" + ] + }, + "ProfileActivity": { + "type": "object", + "properties": { + "events_attended": { + "type": "integer" }, - "typed_rows": { + "events_registered": { + "type": "integer" + }, + "recent_attended_events": { "type": "array", "items": { - "$ref": "#/components/schemas/PaymentCashBookRow" + "$ref": "#/components/schemas/AttendedEvent" + } + } + } + }, + "ProfileError": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "fields": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } } } }, "required": [ - "rows", - "typed_rows" + "code", + "detail", + "fields" ] }, - "PaymentCashBookRow": { + "ProfileGateError": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, - "currency": { + "code": { "type": "string" }, - "method": { + "detail": { "type": "string" }, - "collected_amount": { + "purpose": { + "type": "string" + } + }, + "required": [ + "code", + "detail" + ] + }, + "ProfileImageAttachRequest": { + "type": "object", + "properties": { + "object_key": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "maxLength": 300 }, - "refunded_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "project_id": { + "type": "integer" + } + }, + "required": [ + "object_key" + ] + }, + "ProfileImageUploadRequest": { + "type": "object", + "properties": { + "content_type": { + "type": "string" }, - "net_amount": { + "filename": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "maxLength": 255 + }, + "project_id": { + "type": "integer" } }, "required": [ - "collected_amount", - "currency", - "method", - "net_amount", - "refunded_amount" + "content_type", + "filename" ] }, - "PaymentDocument": { + "ProfileRead": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "payment": { + "membership_id": { "type": "integer" }, - "number": { - "type": "string", - "maxLength": 80 + "display_name": { + "type": "string" }, - "kind": { - "$ref": "#/components/schemas/PaymentDocumentKindEnum" + "is_visible": { + "type": "boolean" }, - "issued_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "show_attended_events": { + "type": "boolean" }, - "money_at": { - "type": "string", - "format": "date-time" + "show_certifications": { + "type": "boolean" }, - "amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "certifications": { + "type": "array", + "items": { + "type": "string" + } }, - "currency": { + "headline": { + "type": "string" + }, + "institution": { + "type": "string" + }, + "bio": { + "type": "string" + }, + "avatar_url": { "type": "string", - "maxLength": 3 + "nullable": true + }, + "interests": {}, + "languages": {}, + "education": {}, + "github_username": { + "type": "string" + }, + "github_contributions": { + "type": "integer", + "nullable": true + }, + "projects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProjectRead" + } + }, + "activity": { + "$ref": "#/components/schemas/ProfileActivity" } }, "required": [ - "amount", - "currency", - "id", - "issued_at", - "kind", - "money_at", - "number", - "payment" - ] - }, - "PaymentDocumentKindEnum": { - "enum": [ - "receipt", - "credit_note" - ], - "type": "string", - "description": "* `receipt` - Receipt\n* `credit_note` - Credit note" - }, - "PaymentFilterError": { - "oneOf": [ - { - "$ref": "#/components/schemas/HardwareRequestError" - }, - { - "$ref": "#/components/schemas/PaymentFilterValidationError" - } + "activity", + "avatar_url", + "bio", + "certifications", + "display_name", + "education", + "github_contributions", + "github_username", + "headline", + "institution", + "interests", + "is_visible", + "languages", + "membership_id", + "projects", + "show_attended_events", + "show_certifications" ] }, - "PaymentFilterValidationError": { + "ProfileWrite": { "type": "object", "properties": { - "due_state": { + "is_visible": { + "type": "boolean" + }, + "show_attended_events": { + "type": "boolean" + }, + "show_certifications": { + "type": "boolean" + }, + "headline": { + "type": "string", + "maxLength": 200 + }, + "institution": { + "type": "string", + "maxLength": 200 + }, + "bio": { + "type": "string", + "maxLength": 2000 + }, + "interests": { "type": "array", "items": { - "type": "string" - } + "type": "string", + "maxLength": 60 + }, + "maxItems": 20 }, - "status": { + "languages": { "type": "array", "items": { - "type": "string" - } + "type": "string", + "maxLength": 60 + }, + "maxItems": 10 }, - "subject_type": { + "education": { "type": "array", "items": { - "type": "string" - } + "$ref": "#/components/schemas/EducationEntry" + }, + "maxItems": 10 }, - "for_recording": { + "github_username": { + "oneOf": [ + { + "type": "string", + "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "projects": { "type": "array", "items": { - "type": "string" - } + "$ref": "#/components/schemas/ProjectWrite" + }, + "maxItems": 20 } } }, - "PaymentListResponse": { + "ProjectLink": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { + "label": { "type": "string", - "format": "uri", - "nullable": true + "maxLength": 60 }, - "previous": { + "url": { "type": "string", "format": "uri", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PaymentReconciliation" - } + "maxLength": 500 } }, "required": [ - "count", - "next", - "previous", - "results" + "label", + "url" ] }, - "PaymentOffline": { + "ProjectRead": { "type": "object", "properties": { - "settlement": { - "$ref": "#/components/schemas/ManualSettlement" + "id": { + "type": "integer" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "links": {}, + "image_url": { + "type": "string", + "nullable": true } }, "required": [ - "settlement" + "description", + "id", + "image_url", + "links", + "title" ] }, - "PaymentOfflineRefund": { + "ProjectWrite": { "type": "object", "properties": { - "amount": { + "id": { + "type": "integer" + }, + "title": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "maxLength": 200 }, - "reason": { + "description": { "type": "string", "default": "", - "maxLength": 255 + "maxLength": 4000 }, - "receipt": { - "$ref": "#/components/schemas/ManualRefundReceipt" + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProjectLink" + }, + "maxItems": 10 } }, "required": [ - "amount", - "receipt" + "title" ] }, - "PaymentReconciliation": { + "Promo": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "subject_type": { - "allOf": [ - { - "$ref": "#/components/schemas/SubjectTypeEnum" - } - ], - "readOnly": true - }, - "subject_id": { + "percent_off": { "type": "integer", "readOnly": true }, - "subject_label": { + "amount_off": { "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/Status66aEnum" - } - ], + "max_uses": { + "type": "integer", "readOnly": true }, - "due_at": { + "uses": { + "type": "integer", + "readOnly": true + }, + "expires_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "due_date": { - "type": "string", - "nullable": true, - "readOnly": true - }, - "due_state": { - "type": "string", - "readOnly": true - }, - "amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true - }, - "currency": { + "revoked_at": { "type": "string", - "readOnly": true - }, - "refunded_amount": { - "type": "number", - "format": "double", - "readOnly": true - }, - "refunds": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Refund" - }, - "readOnly": true - }, - "settlement": { - "allOf": [ - { - "$ref": "#/components/schemas/StaffSettlement" - } - ], - "nullable": true, - "readOnly": true + "format": "date-time", + "readOnly": true, + "nullable": true }, "created_at": { "type": "string", "format": "date-time", "readOnly": true }, - "updated_at": { + "code": { "type": "string", - "format": "date-time", - "readOnly": true + "readOnly": true, + "description": "Returned once on creation only." } }, "required": [ - "amount", + "amount_off", + "code", "created_at", - "currency", - "due_at", - "due_date", - "due_state", + "expires_at", "id", - "refunded_amount", - "refunds", - "settlement", - "status", - "subject_id", - "subject_label", - "subject_type", - "updated_at" - ] - }, - "PaymentReconciliationReport": { - "type": "object", - "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PaymentReconciliationReportRow" - } - } - }, - "required": [ - "rows", - "typed_rows" + "max_uses", + "percent_off", + "revoked_at", + "uses" ] }, - "PaymentReconciliationReportRow": { + "PromoInput": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, - "currency": { - "type": "string" - }, - "subject_type": { - "type": "string" - }, - "status": { - "type": "string" - }, - "payment_count": { - "type": "integer" - }, - "amount_total": { + "code": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "writeOnly": true, + "maxLength": 128, + "minLength": 8 }, - "outstanding_amount": { + "percent_off": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "default": 0 + }, + "amount_off": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "default": "0.00" }, - "settlement_method": { - "type": "string" + "max_uses": { + "type": "integer", + "minimum": 1, + "default": 1 }, - "refunded_amount": { + "expires_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" - } - }, - "required": [ - "amount_total", - "currency", - "outstanding_amount", - "payment_count", - "refunded_amount", - "settlement_method", - "status", - "subject_type" - ] - }, - "PaymentRefundAmend": { - "type": "object", - "properties": { - "receipt": { - "$ref": "#/components/schemas/ManualRefundReceipt" + "format": "date-time", + "nullable": true } - }, - "required": [ - "receipt" - ] + } }, - "PaymentRefundRequest": { + "PromoList": { "type": "object", "properties": { - "amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "count": { + "type": "integer" }, - "reason": { + "next": { "type": "string", - "default": "", - "maxLength": 255 - } - }, - "required": [ - "amount" - ] - }, - "PaymentSettingsError": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - }, - "required": [ - "detail" - ] - }, - "PaymentTimingEnum": { - "enum": [ - "after_activation", - "before_activation" - ], - "type": "string", - "description": "* `after_activation` - Activate then charge\n* `before_activation` - Pay before activation" - }, - "PaymentsLedgerReport": { - "type": "object", - "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "format": "uri", + "nullable": true }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/LedgerLine" - } - } - }, - "required": [ - "rows", - "typed_rows" - ] - }, - "PendingClosure": { - "type": "object", - "properties": { - "digest": { + "previous": { "type": "string", - "maxLength": 64 + "format": "uri", + "nullable": true }, - "identities": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/ClosureIdentity" + "$ref": "#/components/schemas/Promo" } } }, "required": [ - "digest", - "identities" + "count", + "next", + "previous", + "results" ] }, - "PhaseEnum": { - "enum": [ - "issue", - "return" - ], - "type": "string", - "description": "* `issue` - issue\n* `return` - return" - }, - "PhoneConfirm": { + "ProvisionSubdomainRequest": { "type": "object", "properties": { - "phone": { - "type": "string", - "maxLength": 32 - }, - "code": { + "label": { "type": "string", - "maxLength": 6, - "minLength": 6 + "maxLength": 63 } }, "required": [ - "code", - "phone" - ] - }, - "PhoneLoginResult": { - "oneOf": [ - { - "$ref": "#/components/schemas/MFACompleted" - }, - { - "$ref": "#/components/schemas/MFAChallengeResponse" - } + "label" ] }, - "PhoneStart": { + "ProvisionSubdomainValidationError": { "type": "object", "properties": { - "phone": { - "type": "string", - "maxLength": 32 + "label": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "phone" + "label" ] }, - "PhoneStartResponse": { + "PublicAdmission": { "type": "object", "properties": { - "detail": { + "join_mode": { "type": "string" - } - }, - "required": [ - "detail" - ] - }, - "PhoneStatus": { - "type": "object", - "properties": { - "phone_e164": { + }, + "vouches_required": { + "type": "integer" + }, + "application_schema": { + "nullable": true + }, + "default_plan": { + "type": "object", + "additionalProperties": {}, + "nullable": true + }, + "joining_fee": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "joining_fee_currency": { "type": "string" }, - "verified": { - "type": "boolean" + "payment_timing": { + "type": "string" } }, "required": [ - "phone_e164", - "verified" + "application_schema", + "default_plan", + "join_mode", + "joining_fee", + "joining_fee_currency", + "payment_timing", + "vouches_required" ] }, - "PlatformBackupHealth": { - "type": "object", - "properties": { - "automatic_enabled": { - "type": "boolean" - }, - "last_success_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "status": { - "type": "string" - }, - "started_at": { + "PublicAvailabilityInterval": { + "type": "object", + "properties": { + "starts_at": { "type": "string", "format": "date-time", - "nullable": true + "readOnly": true }, - "finished_at": { + "ends_at": { "type": "string", "format": "date-time", - "nullable": true - }, - "has_error": { - "type": "boolean" + "readOnly": true }, - "cadence_minutes": { - "type": "integer" + "booker_name": { + "type": "string", + "readOnly": true, + "nullable": true } }, "required": [ - "automatic_enabled", - "cadence_minutes", - "finished_at", - "has_error", - "last_success_at", - "started_at", - "status" + "booker_name", + "ends_at", + "starts_at" ] }, - "PlatformBackupSettings": { + "PublicAvailabilityModeFc2Enum": { + "enum": [ + "exact_count", + "status_only", + "hidden" + ], + "type": "string", + "description": "* `exact_count` - Exact Count\n* `status_only` - Status Only\n* `hidden` - Hidden" + }, + "PublicBookableSpace": { "type": "object", "properties": { - "automatic_backups_enabled": { - "type": "boolean" + "public_token": { + "type": "string", + "format": "uuid", + "readOnly": true }, - "retention_days": { + "name": { + "type": "string", + "readOnly": true + }, + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/Kind3bfEnum" + } + ], + "readOnly": true + }, + "description": { + "type": "string", + "readOnly": true + }, + "capacity": { "type": "integer", - "maximum": 3650, - "minimum": 1 + "minimum": 0, + "readOnly": true }, - "last_scheduled_at": { + "location": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "readOnly": true }, - "last_success_at": { + "image_url": { "type": "string", - "format": "date-time", + "readOnly": true + }, + "approval_mode": { + "allOf": [ + { + "$ref": "#/components/schemas/ApprovalModeE35Enum" + } + ], + "readOnly": true + }, + "custom_form": { "readOnly": true, "nullable": true }, - "last_error": { - "type": "string", + "show_public_availability": { + "type": "boolean", "readOnly": true }, - "updated_at": { - "type": "string", - "format": "date-time", + "show_public_booker_names": { + "type": "boolean", "readOnly": true } }, "required": [ - "last_error", - "last_scheduled_at", - "last_success_at", - "updated_at" + "approval_mode", + "capacity", + "custom_form", + "description", + "image_url", + "kind", + "location", + "name", + "public_token", + "show_public_availability", + "show_public_booker_names" ] }, - "PlatformDashboard": { + "PublicBookingInput": { "type": "object", "properties": { - "checked_at": { + "certification_override_reason": { "type": "string", - "format": "date-time" - }, - "tenants_by_state": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - }, - "usage_totals": { - "type": "object", - "additionalProperties": { - "type": "integer" - } - }, - "tenants": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PlatformTenantUsage" - } - }, - "backup": { - "$ref": "#/components/schemas/PlatformBackupHealth" + "writeOnly": true, + "maxLength": 1000 }, - "offsite": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PlatformOffsiteHealth" - } + "starts_at": { + "type": "string", + "format": "date-time", + "writeOnly": true }, - "scheduler": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PlatformTaskHealth" - } + "ends_at": { + "type": "string", + "format": "date-time", + "writeOnly": true }, - "failed_deliveries": { - "$ref": "#/components/schemas/FailedDeliveries" + "custom_answers": { + "writeOnly": true, + "nullable": true } }, "required": [ - "backup", - "checked_at", - "failed_deliveries", - "offsite", - "scheduler", - "tenants", - "tenants_by_state", - "usage_totals" + "ends_at", + "starts_at" ] }, - "PlatformEmailSettings": { + "PublicBookingResource": { "type": "object", "properties": { - "id": { - "type": "integer", + "public_token": { + "type": "string", + "format": "uuid", "readOnly": true }, - "smtp_host": { + "name": { "type": "string", - "maxLength": 200 + "readOnly": true }, - "smtp_port": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/Kind3bfEnum" + } + ], + "readOnly": true }, - "smtp_username": { + "description": { "type": "string", - "maxLength": 200 + "readOnly": true }, - "smtp_password": { + "capacity": { + "type": "integer", + "minimum": 0, + "readOnly": true + }, + "location": { "type": "string", - "writeOnly": true + "readOnly": true }, - "smtp_password_set": { + "image_url": { + "type": "string" + }, + "approval_mode": { + "allOf": [ + { + "$ref": "#/components/schemas/ApprovalModeE35Enum" + } + ], + "readOnly": true + }, + "custom_form": { + "readOnly": true, + "nullable": true + }, + "show_public_availability": { "type": "boolean", "readOnly": true }, - "smtp_use_tls": { - "type": "boolean" + "show_public_booker_names": { + "type": "boolean", + "readOnly": true }, - "smtp_use_ssl": { - "type": "boolean" + "payment_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "from_email": { - "oneOf": [ - { - "type": "string", - "format": "email", - "maxLength": 254 - }, + "currency": { + "type": "string" + }, + "rules": { + "$ref": "#/components/schemas/BookingPublicRules" + }, + "website": { + "allOf": [ { - "type": "string", - "maxLength": 0 + "$ref": "#/components/schemas/BookingWebsite" } - ] - }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true + ], + "nullable": true } }, "required": [ - "id", - "smtp_password_set", - "updated_at" + "approval_mode", + "capacity", + "currency", + "custom_form", + "description", + "image_url", + "kind", + "location", + "name", + "payment_amount", + "public_token", + "rules", + "show_public_availability", + "show_public_booker_names", + "website" ] }, - "PlatformEnum": { - "enum": [ - "apple", - "android" - ], - "type": "string", - "description": "* `apple` - Apple\n* `android` - Android" - }, - "PlatformOffsiteHealth": { + "PublicCategory": { "type": "object", "properties": { + "id": { + "type": "integer", + "readOnly": true + }, "name": { - "type": "string" + "type": "string", + "readOnly": true }, - "state": { - "type": "string" + "slug": { + "type": "string", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "verified_at": { + "display_order": { + "type": "integer", + "readOnly": true + }, + "icon": { "type": "string", - "format": "date-time", - "nullable": true + "readOnly": true }, - "has_error": { - "type": "boolean" + "product_count": { + "type": "integer", + "readOnly": true } }, "required": [ - "has_error", + "display_order", + "icon", + "id", "name", - "state", - "verified_at" + "product_count", + "slug" ] }, - "PlatformProblem": { + "PublicConfig": { "type": "object", "properties": { - "key": { - "type": "string" + "email_enabled": { + "type": "boolean" }, - "status": { - "$ref": "#/components/schemas/Status00eEnum" + "public_image_max_bytes": { + "type": "integer" }, - "message": { - "type": "string" + "social_auth": { + "type": "object", + "additionalProperties": {} }, - "fix_url": { - "type": "string" + "phone_login": { + "type": "object", + "additionalProperties": {} }, - "makerspace_id": { - "type": "integer", - "nullable": true + "password_login": { + "type": "object", + "additionalProperties": {} + }, + "self_registration": { + "type": "object", + "additionalProperties": {} + }, + "member_accounts": { + "type": "object", + "additionalProperties": {} + }, + "native": { + "$ref": "#/components/schemas/NativeConfig" } }, "required": [ - "fix_url", - "key", - "makerspace_id", - "message", - "status" + "email_enabled", + "public_image_max_bytes" ] }, - "PlatformProblems": { + "PublicEvent": { "type": "object", "properties": { - "checked_at": { + "participating_organizations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ParticipatingOrganization" + }, + "readOnly": true + }, + "audience_label": { "type": "string", - "format": "date-time" + "nullable": true, + "readOnly": true }, - "status": { - "$ref": "#/components/schemas/Status00eEnum" + "registration_audience": { + "allOf": [ + { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + } + ], + "readOnly": true }, - "results": { + "category": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "readOnly": true + }, + "tags": { "type": "array", "items": { - "$ref": "#/components/schemas/PlatformProblem" - } - } - }, - "required": [ - "checked_at", - "results", - "status" - ] - }, - "PlatformSocialAuthSettings": { - "type": "object", - "properties": { - "google_enabled": { - "type": "boolean", + "type": "string" + }, "readOnly": true }, - "apple_enabled": { + "ticket_types": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "audience": { + "type": "string" + }, + "spots_left": { + "type": "integer", + "nullable": true + } + } + }, + "readOnly": true + }, + "promo_codes_enabled": { "type": "boolean", "readOnly": true }, - "github_enabled": { + "ticket_selection_required": { "type": "boolean", "readOnly": true }, - "google_web_client_id": { + "website": { + "allOf": [ + { + "$ref": "#/components/schemas/Website" + } + ], + "nullable": true, + "readOnly": true + }, + "public_token": { "type": "string", - "maxLength": 255 + "format": "uuid", + "readOnly": true }, - "google_ios_client_id": { + "title": { "type": "string", - "maxLength": 255 + "readOnly": true }, - "google_android_client_id": { + "description": { "type": "string", - "maxLength": 255 + "readOnly": true }, - "apple_service_id": { + "starts_at": { "type": "string", - "maxLength": 255 + "format": "date-time", + "readOnly": true }, - "apple_native_app_ids": {}, - "apple_team_id": { + "ends_at": { "type": "string", - "maxLength": 64 + "format": "date-time", + "readOnly": true }, - "apple_key_id": { + "location": { "type": "string", - "maxLength": 64 + "readOnly": true + }, + "location_kind": { + "allOf": [ + { + "$ref": "#/components/schemas/LocationKindEnum" + } + ], + "readOnly": true + }, + "custom_form": { + "readOnly": true, + "nullable": true + }, + "capacity": { + "type": "integer", + "minimum": 0, + "readOnly": true + }, + "show_registration_count": { + "type": "boolean", + "readOnly": true + }, + "registration_count": { + "type": "integer", + "nullable": true, + "readOnly": true + }, + "payment_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true }, - "apple_private_key": { + "currency": { "type": "string", - "writeOnly": true + "readOnly": true }, - "apple_private_key_set": { + "availability": { + "allOf": [ + { + "$ref": "#/components/schemas/AvailabilityEnum" + } + ], + "readOnly": true + }, + "registration_requires_approval": { "type": "boolean", "readOnly": true }, - "github_client_id": { + "effective_registration_cutoff_at": { "type": "string", - "maxLength": 255 + "format": "date-time", + "nullable": true, + "readOnly": true }, - "github_client_secret": { + "registration_open": { + "type": "boolean", + "readOnly": true + }, + "image_url": { "type": "string", - "writeOnly": true, - "maxLength": 4096 + "format": "uri", + "nullable": true, + "readOnly": true }, - "github_client_secret_set": { - "type": "boolean", + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusE90Enum" + } + ], "readOnly": true }, - "clear_github_client_secret": { - "type": "boolean", - "writeOnly": true + "organizers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventOrganizerSummary" + }, + "readOnly": true }, - "updated_at": { - "type": "string", - "format": "date-time", + "series": { + "type": "object", + "nullable": true, + "properties": { + "public_token": { + "type": "string", + "format": "uuid" + }, + "title": { + "type": "string" + } + }, "readOnly": true } }, "required": [ - "apple_enabled", - "apple_private_key_set", - "github_client_secret_set", - "github_enabled", - "google_enabled", - "updated_at" + "audience_label", + "availability", + "capacity", + "category", + "currency", + "custom_form", + "description", + "effective_registration_cutoff_at", + "ends_at", + "image_url", + "location", + "location_kind", + "organizers", + "participating_organizations", + "payment_amount", + "promo_codes_enabled", + "public_token", + "registration_audience", + "registration_count", + "registration_open", + "registration_requires_approval", + "series", + "show_registration_count", + "starts_at", + "status", + "tags", + "ticket_selection_required", + "ticket_types", + "title", + "website" ] }, - "PlatformStripeConnectSettings": { + "PublicEventRegistrationInput": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "stripe_publishable_key": { - "type": "string", + "custom_answers": { "writeOnly": true, - "maxLength": 255 + "nullable": true }, - "stripe_publishable_key_set": { - "type": "boolean", - "readOnly": true + "host_waiver_id": { + "type": "integer", + "minimum": 1, + "writeOnly": true, + "nullable": true }, - "stripe_secret_key": { + "host_waiver_version": { "type": "string", - "writeOnly": true + "writeOnly": true, + "nullable": true, + "maxLength": 64 }, - "stripe_secret_key_set": { + "host_waiver_accepted": { "type": "boolean", - "readOnly": true + "writeOnly": true, + "default": false }, - "stripe_webhook_secret": { + "phone": { "type": "string", - "writeOnly": true + "writeOnly": true, + "maxLength": 64 }, - "stripe_webhook_secret_set": { - "type": "boolean", - "readOnly": true + "email": { + "writeOnly": true, + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "stripe_connect_client_id": { + "invite_code": { "type": "string", - "maxLength": 255 + "writeOnly": true, + "maxLength": 128 }, - "application_fee_bps": { + "ticket_type_id": { "type": "integer", - "maximum": 10000, - "minimum": 0 + "minimum": 1, + "writeOnly": true }, - "updated_at": { + "promo_code": { "type": "string", - "format": "date-time", + "writeOnly": true, + "maxLength": 128 + } + } + }, + "PublicEventRegistrationResponse": { + "type": "object", + "properties": { + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/PublicEventRegistrationResponseStatusEnum" + } + ], "readOnly": true } }, "required": [ - "id", - "stripe_publishable_key_set", - "stripe_secret_key_set", - "stripe_webhook_secret_set", - "updated_at" + "status" ] }, - "PlatformTaskHealth": { + "PublicEventRegistrationResponseStatusEnum": { + "enum": [ + "pending_approval", + "registered", + "waitlisted" + ], + "type": "string", + "description": "* `pending_approval` - pending_approval\n* `registered` - registered\n* `waitlisted` - waitlisted" + }, + "PublicImageAttachRequest": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "task": { - "type": "string" - }, - "cadence_minutes": { - "type": "integer" - }, - "last_run_at": { + "object_key": { "type": "string", - "format": "date-time", - "nullable": true - }, - "status": { - "$ref": "#/components/schemas/Status00eEnum" + "maxLength": 300 } }, "required": [ - "cadence_minutes", - "last_run_at", - "name", - "status", - "task" + "object_key" ] }, - "PlatformTenantUsage": { + "PublicImageUploadRequest": { "type": "object", "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "slug": { + "content_type": { "type": "string" }, - "usage": { - "$ref": "#/components/schemas/MakerspaceUsage" + "filename": { + "type": "string", + "maxLength": 255 } }, "required": [ - "id", - "name", - "slug", - "usage" + "content_type", + "filename" ] }, - "Policy": { - "type": "object", - "properties": { - "blind_default": { - "type": "boolean" - }, - "variance_threshold": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true - } - } - }, - "PreferenceCell": { + "PublicImageUploadResponse": { "type": "object", "properties": { - "feature": { - "$ref": "#/components/schemas/Feature9d8Enum" - }, - "channel": { - "$ref": "#/components/schemas/Channel673Enum" + "object_key": { + "type": "string" }, - "enabled": { - "type": "boolean" + "url": { + "type": "string", + "format": "uri" }, - "daily_digest": { - "type": "boolean" + "fields": { + "type": "object", + "additionalProperties": {} }, - "available": { - "type": "boolean" + "method": { + "type": "string" }, - "mandatory_events": { - "type": "array", - "items": { - "type": "string" - } + "headers": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "available", - "channel", - "enabled", - "feature", - "mandatory_events" + "object_key", + "url" ] }, - "PreferenceResponse": { + "PublicInventoryError": { "type": "object", "properties": { - "cells": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PreferenceCell" - } + "detail": { + "type": "string" + }, + "module": { + "type": "string" + }, + "feature": { + "type": "string" + }, + "code": { + "type": "string" } - }, - "required": [ - "cells" - ] + } }, - "PresenceCurrent": { + "PublicLandingPage6a0Enum": { + "enum": [ + "auto", + "inventory", + "events", + "machines", + "bookings", + "membership" + ], + "type": "string", + "description": "* `auto` - Auto\n* `inventory` - Inventory\n* `events` - Events\n* `machines` - Machines\n* `bookings` - Bookings\n* `membership` - Membership" + }, + "PublicMachine": { "type": "object", + "description": "Allowlist-only public projection; never subclass the staff serializer.", "properties": { - "active": { - "type": "boolean" + "name": { + "type": "string", + "readOnly": true }, - "session": { + "machine_type": { "allOf": [ { - "$ref": "#/components/schemas/PresenceSession" + "$ref": "#/components/schemas/PublicMachineType" } ], - "nullable": true + "readOnly": true + }, + "image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "readOnly": true + }, + "status": { + "type": "string", + "readOnly": true + }, + "usage_hours": { + "type": "string", + "format": "decimal", + "example": "12.50", + "readOnly": true } }, "required": [ - "active", - "session" + "image_url", + "machine_type", + "name", + "status", + "usage_hours" ] }, - "PresenceRoster": { + "PublicMachineDetail": { "type": "object", + "description": "Allowlist-only public projection; never subclass the staff serializer.", "properties": { - "display_name": { - "type": "string" + "name": { + "type": "string", + "readOnly": true }, - "role_label": { + "machine_type": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "icon": { + "type": "string" + } + }, + "readOnly": true + }, + "image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "readOnly": true + }, + "status": { "type": "string", "readOnly": true }, - "started_at": { + "usage_hours": { "type": "string", - "format": "date-time" + "format": "decimal", + "readOnly": true }, - "expires_at": { + "id": { + "type": "integer", + "readOnly": true + }, + "status_since": { "type": "string", - "format": "date-time" + "format": "date-time", + "readOnly": true + }, + "training_required": { + "type": "boolean", + "nullable": true, + "readOnly": true + }, + "queue": { + "allOf": [ + { + "$ref": "#/components/schemas/PublicQueueEstimate" + } + ], + "nullable": true, + "readOnly": true } }, "required": [ - "display_name", - "expires_at", - "role_label", - "started_at" + "id", + "image_url", + "machine_type", + "name", + "queue", + "status", + "status_since", + "training_required", + "usage_hours" ] }, - "PresenceSession": { + "PublicMachineServiceSubmit": { "type": "object", "properties": { - "started_at": { + "certification_override_reason": { "type": "string", - "format": "date-time" + "writeOnly": true, + "maxLength": 1000 }, - "expires_at": { - "type": "string", - "format": "date-time" + "website": { + "type": "string" }, - "ended_at": { + "machine_id": { + "type": "integer", + "minimum": 1 + }, + "title": { "type": "string", - "format": "date-time", - "nullable": true + "maxLength": 200 }, - "end_reason": { + "description": { + "type": "string" + }, + "source_link": { "oneOf": [ { - "$ref": "#/components/schemas/EndReasonEnum" + "type": "string", + "format": "uri", + "maxLength": 200 }, { - "$ref": "#/components/schemas/BlankEnum" + "type": "string", + "maxLength": 0 } ] }, - "active": { - "type": "boolean", - "readOnly": true - } + "capability_payload": {} }, "required": [ - "active", - "expires_at", - "started_at" + "machine_id", + "title" ] }, - "PresenceStart": { + "PublicMachineServiceSubmitResponse": { "type": "object", "properties": { - "duration_minutes": { - "type": "integer", - "maximum": 480, - "minimum": 15 - }, - "latitude": { - "type": "number", - "format": "double", - "maximum": 90, - "minimum": -90 - }, - "longitude": { - "type": "number", - "format": "double", - "maximum": 180, - "minimum": -180 + "public_token": { + "type": "string", + "format": "uuid" }, - "accuracy": { - "type": "number", - "format": "double", - "minimum": 0 + "status": { + "type": "string" } }, "required": [ - "duration_minutes" + "public_token", + "status" ] }, - "PresetEnum": { - "enum": [ - "sheet", - "single" - ], - "type": "string", - "description": "* `sheet` - sheet\n* `single` - single" - }, - "PrinterPool": { + "PublicMachineType": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "machine_id": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "machine_type_id": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "material": { + "name": { "type": "string", - "maxLength": 100 + "readOnly": true }, - "color": { + "icon": { "type": "string", - "maxLength": 100 - }, - "color_hex": { + "readOnly": true + } + }, + "required": [ + "icon", + "name" + ] + }, + "PublicMakerspace": { + "type": "object", + "properties": { + "name": { "type": "string", - "maxLength": 7 + "readOnly": true }, - "brand": { + "public_code": { "type": "string", - "maxLength": 100 + "readOnly": true }, - "lot_code": { + "slug": { "type": "string", - "maxLength": 100 - }, - "unit": { - "$ref": "#/components/schemas/PrinterPoolUnitEnum" + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "initial_grams": { + "location": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "readOnly": true }, - "remaining_grams": { + "map_url": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "low_threshold_grams": { + "logo_url": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "nullable": true - }, - "is_active": { - "type": "boolean", + "format": "uri", + "nullable": true, "readOnly": true }, - "is_public": { - "type": "boolean" - }, - "opened_at": { + "cover_image_url": { "type": "string", - "format": "date-time", + "format": "uri", + "nullable": true, + "readOnly": true + } + }, + "required": [ + "cover_image_url", + "location", + "logo_url", + "map_url", + "name", + "public_code", + "slug" + ] + }, + "PublicMaterial": { + "type": "object", + "properties": { + "id": { + "type": "integer", "nullable": true }, - "created_at": { + "name": { + "type": "string" + }, + "colours": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "colours", + "id", + "name" + ] + }, + "PublicOpeningInterval": { + "type": "object", + "properties": { + "starts_at": { "type": "string", "format": "date-time", "readOnly": true }, - "updated_at": { + "ends_at": { "type": "string", "format": "date-time", "readOnly": true } }, "required": [ - "created_at", - "id", - "initial_grams", - "is_active", - "machine_id", - "machine_type_id", - "material", - "remaining_grams", - "updated_at" + "ends_at", + "starts_at" ] }, - "PrinterPoolCorrection": { + "PublicOrganization": { "type": "object", "properties": { - "quantity_delta": { + "slug": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "reason": { - "type": "string" + "name": { + "type": "string", + "readOnly": true + }, + "description": { + "type": "string", + "readOnly": true + }, + "website": { + "type": "string", + "format": "uri", + "readOnly": true + }, + "logo_url": { + "type": "string", + "format": "uri", + "nullable": true, + "readOnly": true + }, + "catalogue_links": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "readOnly": true } }, "required": [ - "quantity_delta", - "reason" + "catalogue_links", + "description", + "logo_url", + "name", + "slug", + "website" ] }, - "PrinterPoolCreate": { + "PublicOrganizationEvent": { "type": "object", "properties": { - "machine_id": { - "type": "integer", - "minimum": 1, - "nullable": true + "participating_organizations": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ParticipatingOrganization" + }, + "readOnly": true }, - "machine_type_id": { - "type": "integer", - "minimum": 1, - "nullable": true + "audience_label": { + "type": "string", + "nullable": true, + "readOnly": true }, - "is_public": { - "type": "boolean", - "default": true + "registration_audience": { + "allOf": [ + { + "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" + } + ], + "readOnly": true }, - "material": { - "type": "string", - "maxLength": 100 + "category": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "readOnly": true }, - "color": { - "type": "string", - "maxLength": 100 + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true }, - "color_hex": { - "type": "string", - "maxLength": 7 + "ticket_types": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "amount": { + "type": "string" + }, + "audience": { + "type": "string" + }, + "spots_left": { + "type": "integer", + "nullable": true + } + } + }, + "readOnly": true }, - "brand": { - "type": "string", - "maxLength": 100 + "promo_codes_enabled": { + "type": "boolean", + "readOnly": true }, - "lot_code": { - "type": "string", - "maxLength": 100 + "ticket_selection_required": { + "type": "boolean", + "readOnly": true }, - "unit": { + "website": { "allOf": [ { - "$ref": "#/components/schemas/PrinterPoolCreateUnitEnum" + "$ref": "#/components/schemas/Website" } ], - "default": "grams" + "nullable": true, + "readOnly": true }, - "quantity": { + "public_token": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "uuid", + "readOnly": true }, - "initial_grams": { + "title": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "readOnly": true }, - "low_threshold_grams": { + "description": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "nullable": true - } - }, - "required": [ - "material" - ] - }, - "PrinterPoolCreateUnitEnum": { - "enum": [ - "grams", - "milliliters", - "millimeters", - "count" - ], - "type": "string", - "description": "* `grams` - grams\n* `milliliters` - milliliters\n* `millimeters` - millimeters\n* `count` - count" - }, - "PrinterPoolUnitEnum": { - "enum": [ - "grams", - "milliliters", - "millimeters", - "count" - ], - "type": "string", - "description": "* `grams` - Grams\n* `milliliters` - Milliliters\n* `millimeters` - Millimeters\n* `count` - Count" - }, - "PrinterServiceReport": { - "type": "object", - "properties": { - "printer_metrics": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PrinterServiceReportRow" - } - } - }, - "required": [ - "printer_metrics" - ] - }, - "PrinterServiceReportRow": { - "type": "object", - "properties": { - "makerspace_id": { - "type": "integer" + "readOnly": true }, - "machine_id": { - "type": "integer" + "starts_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "machine_name": { - "type": "string" + "ends_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "model": { - "type": "string" + "location": { + "type": "string", + "readOnly": true }, - "completed_hours": { - "type": "number", - "format": "double" + "location_kind": { + "allOf": [ + { + "$ref": "#/components/schemas/LocationKindEnum" + } + ], + "readOnly": true }, - "failed_partial_hours": { - "type": "number", - "format": "double" + "custom_form": { + "readOnly": true, + "nullable": true }, - "manual_hours": { - "type": "number", - "format": "double" + "capacity": { + "type": "integer", + "minimum": 0, + "readOnly": true }, - "consumed_grams": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "show_registration_count": { + "type": "boolean", + "readOnly": true }, - "payment_due": { + "registration_count": { + "type": "integer", + "nullable": true, + "readOnly": true + }, + "payment_amount": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true }, - "payment_paid": { + "currency": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - } - }, - "required": [ - "completed_hours", - "consumed_grams", - "failed_partial_hours", - "machine_id", - "machine_name", - "manual_hours", - "model", - "payment_due", - "payment_paid" - ] - }, - "ProblemReportTriage": { - "type": "object", - "properties": { - "outcome": { - "$ref": "#/components/schemas/ProblemReportTriageOutcomeEnum" + "readOnly": true }, - "resolutions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProblemReportTriageResolution" - } + "availability": { + "allOf": [ + { + "$ref": "#/components/schemas/AvailabilityEnum" + } + ], + "readOnly": true }, - "note": { + "registration_requires_approval": { + "type": "boolean", + "readOnly": true + }, + "effective_registration_cutoff_at": { "type": "string", - "default": "" + "format": "date-time", + "nullable": true, + "readOnly": true }, - "evidence_id": { - "type": "integer", - "nullable": true - } - }, - "required": [ - "outcome" - ] - }, - "ProblemReportTriageOutcomeEnum": { - "enum": [ - "no_issue", - "damaged", - "missing", - "needs_fix" - ], - "type": "string", - "description": "* `no_issue` - no_issue\n* `damaged` - damaged\n* `missing` - missing\n* `needs_fix` - needs_fix" - }, - "ProblemReportTriageResolution": { - "type": "object", - "properties": { - "item_id": { - "type": "integer" + "registration_open": { + "type": "boolean", + "readOnly": true }, - "quantity": { - "type": "integer", - "minimum": 1 - } - }, - "required": [ - "item_id", - "quantity" - ] - }, - "ProblemReportTriageResponse": { - "type": "object", - "properties": { - "id": { - "type": "integer" + "image_url": { + "type": "string", + "format": "uri", + "nullable": true, + "readOnly": true }, - "outcome": { - "type": "string" + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusE90Enum" + } + ], + "readOnly": true }, - "resolved": { - "type": "boolean" + "organizers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EventOrganizerSummary" + }, + "readOnly": true + }, + "series": { + "type": "object", + "nullable": true, + "properties": { + "public_token": { + "type": "string", + "format": "uuid" + }, + "title": { + "type": "string" + } + }, + "readOnly": true + }, + "host": { + "allOf": [ + { + "$ref": "#/components/schemas/OrganizationEventHost" + } + ], + "readOnly": true } }, "required": [ - "id", - "outcome", - "resolved" + "audience_label", + "availability", + "capacity", + "category", + "currency", + "custom_form", + "description", + "effective_registration_cutoff_at", + "ends_at", + "host", + "image_url", + "location", + "location_kind", + "organizers", + "participating_organizations", + "payment_amount", + "promo_codes_enabled", + "public_token", + "registration_audience", + "registration_count", + "registration_open", + "registration_requires_approval", + "series", + "show_registration_count", + "starts_at", + "status", + "tags", + "ticket_selection_required", + "ticket_types", + "title", + "website" ] }, - "ProductQrHistory": { + "PublicOrganizationEventList": { "type": "object", "properties": { - "product": { + "count": { "type": "integer" }, - "scans": { + "next": { + "type": "string", + "nullable": true + }, + "previous": { + "type": "string", + "nullable": true + }, + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/QrHistoryItem" + "$ref": "#/components/schemas/PublicOrganizationEvent" } } }, "required": [ - "product", - "scans" + "count", + "next", + "previous", + "results" ] }, - "ProfileActivity": { + "PublicPrintStatusLookupPolicyEnum": { + "enum": [ + "token_only", + "email_unverified" + ], + "type": "string", + "description": "* `token_only` - Token only\n* `email_unverified` - Email unverified" + }, + "PublicPrinterPool": { "type": "object", "properties": { - "events_attended": { - "type": "integer" - }, - "events_registered": { + "id": { "type": "integer" }, - "recent_attended_events": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AttendedEvent" - } - } - } - }, - "ProfileError": { - "type": "object", - "properties": { - "code": { + "material": { "type": "string" }, - "detail": { + "color": { "type": "string" - }, - "fields": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } } }, "required": [ - "code", - "detail", - "fields" + "color", + "id", + "material" ] }, - "ProfileGateError": { + "PublicPrinterQueue": { "type": "object", "properties": { - "code": { - "type": "string" + "machine_type_id": { + "type": "integer" }, - "detail": { + "id": { + "type": "integer" + }, + "name": { "type": "string" }, - "purpose": { + "description": { "type": "string" } }, "required": [ - "code", - "detail" + "description", + "id", + "machine_type_id", + "name" ] }, - "ProfileImageAttachRequest": { + "PublicPrinterStatus": { "type": "object", + "description": "The token endpoint's complete field allowlist. Do not add contact fields.", "properties": { - "object_key": { + "public_token": { "type": "string", - "maxLength": 300 + "format": "uuid" }, - "project_id": { - "type": "integer" - } - }, - "required": [ - "object_key" - ] - }, - "ProfileImageUploadRequest": { - "type": "object", - "properties": { - "content_type": { + "status": { "type": "string" }, - "filename": { - "type": "string", - "maxLength": 255 - }, - "project_id": { - "type": "integer" - } - }, - "required": [ - "content_type", - "filename" - ] - }, - "ProfileRead": { - "type": "object", - "properties": { - "membership_id": { - "type": "integer" - }, - "display_name": { + "title": { "type": "string" }, - "is_visible": { - "type": "boolean" - }, - "show_attended_events": { - "type": "boolean" - }, - "show_certifications": { - "type": "boolean" - }, - "certifications": { - "type": "array", - "items": { - "type": "string" - } - }, - "headline": { - "type": "string" + "created_at": { + "type": "string", + "format": "date-time" }, - "institution": { - "type": "string" + "accepted_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "bio": { - "type": "string" + "started_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "avatar_url": { + "completed_at": { "type": "string", + "format": "date-time", "nullable": true }, - "interests": {}, - "languages": {}, - "education": {}, - "github_username": { - "type": "string" + "estimated_minutes": { + "type": "integer" }, - "github_contributions": { + "queue_position": { "type": "integer", - "nullable": true + "nullable": true, + "readOnly": true }, - "projects": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProjectRead" - } + "queue_approved_ahead": { + "type": "integer", + "nullable": true, + "readOnly": true }, - "activity": { - "$ref": "#/components/schemas/ProfileActivity" + "queue_awaiting_review_ahead": { + "type": "integer", + "nullable": true, + "readOnly": true } }, "required": [ - "activity", - "avatar_url", - "bio", - "certifications", - "display_name", - "education", - "github_contributions", - "github_username", - "headline", - "institution", - "interests", - "is_visible", - "languages", - "membership_id", - "projects", - "show_attended_events", - "show_certifications" + "accepted_at", + "completed_at", + "created_at", + "estimated_minutes", + "public_token", + "queue_approved_ahead", + "queue_awaiting_review_ahead", + "queue_position", + "started_at", + "status", + "title" ] }, - "ProfileWrite": { + "PublicPrinterSubmit": { "type": "object", "properties": { - "is_visible": { - "type": "boolean" + "certification_override_reason": { + "type": "string", + "writeOnly": true, + "maxLength": 1000 }, - "show_attended_events": { - "type": "boolean" + "website": { + "type": "string" }, - "show_certifications": { - "type": "boolean" + "queue_id": { + "type": "integer", + "nullable": true }, - "headline": { + "title": { "type": "string", "maxLength": 200 }, - "institution": { + "project_brief": { + "type": "string" + }, + "preferred_settings": { + "nullable": true + }, + "material": { "type": "string", - "maxLength": 200 + "maxLength": 100 }, - "bio": { + "color": { "type": "string", - "maxLength": 2000 + "maxLength": 100 }, - "interests": { - "type": "array", - "items": { - "type": "string", - "maxLength": 60 - }, - "maxItems": 20 + "consumable_pool_id": { + "type": "integer", + "nullable": true }, - "languages": { - "type": "array", - "items": { - "type": "string", - "maxLength": 60 - }, - "maxItems": 10 + "estimated_filament_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,6}(?:\\.\\d{0,2})?$", + "nullable": true }, - "education": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EducationEntry" - }, - "maxItems": 10 + "quantity": { + "type": "integer", + "minimum": 1, + "default": 1 }, - "github_username": { + "source_link": { "oneOf": [ { "type": "string", - "pattern": "^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$" + "format": "uri", + "maxLength": 200 }, { "type": "string", @@ -95596,277 +118749,216 @@ } ] }, - "projects": { + "file_ids": { "type": "array", "items": { - "$ref": "#/components/schemas/ProjectWrite" - }, - "maxItems": 20 - } - } - }, - "ProjectLink": { - "type": "object", - "properties": { - "label": { - "type": "string", - "maxLength": 60 - }, - "url": { - "type": "string", - "format": "uri", - "maxLength": 500 + "type": "integer" + } } }, "required": [ - "label", - "url" + "title" ] }, - "ProjectRead": { + "PublicPrinterSubmitResponse": { "type": "object", "properties": { - "id": { - "type": "integer" - }, - "title": { - "type": "string" + "public_token": { + "type": "string", + "format": "uuid" }, - "description": { + "status": { "type": "string" - }, - "links": {}, - "image_url": { - "type": "string", - "nullable": true } }, "required": [ - "description", - "id", - "image_url", - "links", - "title" + "public_token", + "status" ] }, - "ProjectWrite": { + "PublicPrinterUpload": { "type": "object", "properties": { - "id": { - "type": "integer" + "queue_id": { + "type": "integer", + "nullable": true }, - "title": { - "type": "string", - "maxLength": 200 + "kind": { + "$ref": "#/components/schemas/PublicPrinterUploadKindEnum" }, - "description": { + "filename": { "type": "string", - "default": "", - "maxLength": 4000 + "maxLength": 255 }, - "links": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ProjectLink" - }, - "maxItems": 10 + "content_type": { + "type": "string", + "maxLength": 128 } }, "required": [ - "title" + "filename", + "kind" ] }, - "Promo": { + "PublicPrinterUploadKindEnum": { + "enum": [ + "stl", + "screenshot" + ], + "type": "string", + "description": "* `stl` - stl\n* `screenshot` - screenshot" + }, + "PublicProduct": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "percent_off": { - "type": "integer", - "readOnly": true - }, - "amount_off": { + "name": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "max_uses": { - "type": "integer", + "description": { + "type": "string", "readOnly": true }, - "uses": { + "category_id": { "type": "integer", - "readOnly": true - }, - "expires_at": { - "type": "string", - "format": "date-time", "readOnly": true, "nullable": true }, - "revoked_at": { + "category_name": { "type": "string", - "format": "date-time", "readOnly": true, "nullable": true }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "code": { + "category_slug": { "type": "string", "readOnly": true, - "description": "Returned once on creation only." - } - }, - "required": [ - "amount_off", - "code", - "created_at", - "expires_at", - "id", - "max_uses", - "percent_off", - "revoked_at", - "uses" - ] - }, - "PromoInput": { - "type": "object", - "properties": { - "code": { - "type": "string", - "writeOnly": true, - "maxLength": 128, - "minLength": 8 - }, - "percent_off": { - "type": "integer", - "maximum": 100, - "minimum": 0, - "default": 0 - }, - "amount_off": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "default": "0.00" - }, - "max_uses": { - "type": "integer", - "minimum": 1, - "default": 1 - }, - "expires_at": { - "type": "string", - "format": "date-time", "nullable": true - } - } - }, - "PromoList": { - "type": "object", - "properties": { - "count": { - "type": "integer" }, - "next": { + "borrowing_terms": { "type": "string", - "format": "uri", - "nullable": true + "readOnly": true + }, + "availability": { + "type": "object", + "nullable": true, + "properties": { + "mode": { + "type": "string", + "enum": [ + "status_only", + "exact_count" + ] + }, + "label": { + "type": "string", + "enum": [ + "Available", + "Limited", + "Unavailable" + ] + }, + "count": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "mode" + ], + "readOnly": true }, - "previous": { + "image_url": { "type": "string", "format": "uri", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Promo" - } + "nullable": true, + "readOnly": true } }, "required": [ - "count", - "next", - "previous", - "results" + "availability", + "borrowing_terms", + "category_id", + "category_name", + "category_slug", + "description", + "id", + "image_url", + "name" ] }, - "ProvisionSubdomainRequest": { + "PublicQueueEstimate": { "type": "object", "properties": { - "label": { - "type": "string", - "maxLength": 63 + "length": { + "type": "integer", + "minimum": 0 + }, + "eta_minutes": { + "type": "integer", + "minimum": 0, + "nullable": true } }, "required": [ - "label" + "eta_minutes", + "length" ] }, - "ProvisionSubdomainValidationError": { + "PublicRequestItemStatus": { "type": "object", "properties": { - "label": { - "type": "array", - "items": { - "type": "string" - } + "product_name": { + "type": "string", + "readOnly": true + }, + "requested_quantity": { + "type": "integer", + "readOnly": true } }, "required": [ - "label" + "product_name", + "requested_quantity" ] }, - "PublicAdmission": { + "PublicRequestStatus": { "type": "object", "properties": { - "join_mode": { - "type": "string" - }, - "vouches_required": { - "type": "integer" - }, - "application_schema": { - "nullable": true - }, - "default_plan": { - "type": "object", - "additionalProperties": {}, - "nullable": true - }, - "joining_fee": { + "status": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "readOnly": true }, - "joining_fee_currency": { - "type": "string" + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "payment_timing": { - "type": "string" + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicRequestItemStatus" + }, + "readOnly": true } }, "required": [ - "application_schema", - "default_plan", - "join_mode", - "joining_fee", - "joining_fee_currency", - "payment_timing", - "vouches_required" + "created_at", + "items", + "status" ] }, - "PublicAvailabilityInterval": { + "PublicSpaceAvailability": { "type": "object", "properties": { + "public_token": { + "type": "string", + "format": "uuid", + "readOnly": true + }, "starts_at": { "type": "string", "format": "date-time", @@ -95877,3265 +118969,3229 @@ "format": "date-time", "readOnly": true }, - "booker_name": { + "timezone": { "type": "string", + "readOnly": true + }, + "opening_intervals": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicOpeningInterval" + }, + "readOnly": true, + "description": "Open windows in UTC, accounting for makerspace hours and dated exceptions." + }, + "availability": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicAvailabilityInterval" + }, "readOnly": true, "nullable": true } }, "required": [ - "booker_name", + "availability", "ends_at", - "starts_at" + "opening_intervals", + "public_token", + "starts_at", + "timezone" ] }, - "PublicAvailabilityModeFc2Enum": { - "enum": [ - "exact_count", - "status_only", - "hidden" - ], - "type": "string", - "description": "* `exact_count` - Exact Count\n* `status_only` - Status Only\n* `hidden` - Hidden" - }, - "PublicBookableSpace": { + "PublicStats": { "type": "object", "properties": { - "public_token": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "name": { - "type": "string", - "readOnly": true - }, - "kind": { + "machines": { "allOf": [ { - "$ref": "#/components/schemas/Kind3bfEnum" + "$ref": "#/components/schemas/PublicStatsMachines" } ], - "readOnly": true - }, - "description": { - "type": "string", - "readOnly": true - }, - "capacity": { - "type": "integer", - "minimum": 0, - "readOnly": true - }, - "location": { - "type": "string", - "readOnly": true - }, - "image_url": { - "type": "string", - "readOnly": true + "readOnly": true, + "nullable": true }, - "approval_mode": { + "printing": { "allOf": [ { - "$ref": "#/components/schemas/ApprovalModeE35Enum" + "$ref": "#/components/schemas/PublicStatsPrinting" } ], - "readOnly": true - }, - "custom_form": { "readOnly": true, "nullable": true }, - "show_public_availability": { - "type": "boolean", + "hardware": { + "allOf": [ + { + "$ref": "#/components/schemas/PublicStatsHardware" + } + ], "readOnly": true }, - "show_public_booker_names": { - "type": "boolean", + "current_loans": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicStatsCurrentLoan" + }, "readOnly": true } }, "required": [ - "approval_mode", - "capacity", - "custom_form", - "description", - "image_url", - "kind", - "location", - "name", - "public_token", - "show_public_availability", - "show_public_booker_names" + "current_loans", + "hardware", + "machines", + "printing" ] }, - "PublicBookingInput": { + "PublicStatsBrand": { "type": "object", "properties": { - "certification_override_reason": { - "type": "string", - "writeOnly": true, - "maxLength": 1000 - }, - "starts_at": { - "type": "string", - "format": "date-time", - "writeOnly": true - }, - "ends_at": { + "brand": { "type": "string", - "format": "date-time", - "writeOnly": true + "readOnly": true }, - "custom_answers": { - "writeOnly": true, - "nullable": true + "grams": { + "type": "number", + "format": "double", + "readOnly": true } }, "required": [ - "ends_at", - "starts_at" + "brand", + "grams" ] }, - "PublicBookingResponse": { + "PublicStatsBusiestPrinter": { "type": "object", "properties": { - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicBookingResponseStatusEnum" - } - ], + "name": { + "type": "string", + "readOnly": true + }, + "model": { + "type": "string", + "readOnly": true + }, + "hours": { + "type": "number", + "format": "double", + "readOnly": true + }, + "completed": { + "type": "integer", "readOnly": true + }, + "image_url": { + "type": "string", + "format": "uri", + "readOnly": true, + "nullable": true } }, "required": [ - "status" + "completed", + "hours", + "image_url", + "model", + "name" ] }, - "PublicBookingResponseStatusEnum": { - "enum": [ - "pending", - "confirmed" - ], - "type": "string", - "description": "* `pending` - pending\n* `confirmed` - confirmed" - }, - "PublicCategory": { + "PublicStatsCurrentLoan": { "type": "object", "properties": { - "id": { - "type": "integer", + "item_name": { + "type": "string", "readOnly": true }, - "name": { + "holder_name": { "type": "string", "readOnly": true }, - "slug": { + "due": { "type": "string", + "format": "date-time", "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "display_order": { - "type": "integer", - "readOnly": true + "nullable": true }, - "icon": { + "since": { "type": "string", - "readOnly": true - }, - "product_count": { - "type": "integer", - "readOnly": true + "format": "date-time", + "readOnly": true, + "nullable": true } }, "required": [ - "display_order", - "icon", - "id", - "name", - "product_count", - "slug" + "due", + "holder_name", + "item_name", + "since" ] }, - "PublicConfig": { + "PublicStatsFilamentTrend": { "type": "object", "properties": { - "email_enabled": { - "type": "boolean" - }, - "public_image_max_bytes": { - "type": "integer" - }, - "social_auth": { - "type": "object", - "additionalProperties": {} - }, - "phone_login": { - "type": "object", - "additionalProperties": {} - }, - "password_login": { - "type": "object", - "additionalProperties": {} - }, - "self_registration": { - "type": "object", - "additionalProperties": {} + "period": { + "type": "string", + "readOnly": true }, - "member_accounts": { - "type": "object", - "additionalProperties": {} + "grams": { + "type": "number", + "format": "double", + "readOnly": true } }, "required": [ - "email_enabled", - "public_image_max_bytes" + "grams", + "period" ] }, - "PublicEvent": { + "PublicStatsHardware": { "type": "object", "properties": { - "registration_audience": { - "allOf": [ - { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" - } - ], - "readOnly": true - }, - "category": { - "type": "object", - "nullable": true, - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "slug": { - "type": "string" - } + "most_popular": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicStatsPopularHardware" }, "readOnly": true }, - "tags": { + "tools_out": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/PublicStatsToolsOut" }, "readOnly": true }, - "ticket_types": { + "library": { + "allOf": [ + { + "$ref": "#/components/schemas/PublicStatsLibrary" + } + ], + "readOnly": true + }, + "recently_added": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "audience": { - "type": "string" - }, - "spots_left": { - "type": "integer", - "nullable": true - } - } + "$ref": "#/components/schemas/PublicStatsRecentlyAdded" }, "readOnly": true - }, - "promo_codes_enabled": { - "type": "boolean", + } + }, + "required": [ + "library", + "most_popular", + "recently_added", + "tools_out" + ] + }, + "PublicStatsJobs": { + "type": "object", + "properties": { + "completed": { + "type": "integer", "readOnly": true }, - "ticket_selection_required": { - "type": "boolean", + "status_counts": { + "allOf": [ + { + "$ref": "#/components/schemas/PublicStatsStatusCounts" + } + ], "readOnly": true }, - "public_token": { - "type": "string", - "format": "uuid", + "queue": { + "allOf": [ + { + "$ref": "#/components/schemas/PublicStatsQueue" + } + ], + "readOnly": true + } + }, + "required": [ + "completed", + "queue", + "status_counts" + ] + }, + "PublicStatsLibrary": { + "type": "object", + "properties": { + "currently_out_count": { + "type": "integer", "readOnly": true }, - "title": { - "type": "string", + "library_size": { + "type": "integer", "readOnly": true }, - "description": { + "available_count": { + "type": "integer", + "readOnly": true + } + }, + "required": [ + "available_count", + "currently_out_count", + "library_size" + ] + }, + "PublicStatsMachines": { + "type": "object", + "properties": { + "usage_hours": { + "type": "number", + "format": "double", + "readOnly": true + } + }, + "required": [ + "usage_hours" + ] + }, + "PublicStatsPopularHardware": { + "type": "object", + "properties": { + "name": { "type": "string", "readOnly": true }, - "starts_at": { - "type": "string", - "format": "date-time", + "times_lent": { + "type": "integer", "readOnly": true }, - "ends_at": { + "total_quantity_lent": { + "type": "integer", + "readOnly": true + } + }, + "required": [ + "name", + "times_lent", + "total_quantity_lent" + ] + }, + "PublicStatsPrinter": { + "type": "object", + "properties": { + "name": { "type": "string", - "format": "date-time", "readOnly": true }, - "location": { + "model": { "type": "string", "readOnly": true }, - "location_kind": { - "allOf": [ - { - "$ref": "#/components/schemas/LocationKindEnum" - } - ], + "jobs": { + "type": "integer", "readOnly": true }, - "custom_form": { - "readOnly": true, - "nullable": true + "hours": { + "type": "number", + "format": "double", + "readOnly": true }, - "capacity": { - "type": "integer", - "minimum": 0, + "grams": { + "type": "number", + "format": "double", "readOnly": true }, - "payment_amount": { + "image_url": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "format": "uri", + "readOnly": true, + "nullable": true + } + }, + "required": [ + "grams", + "hours", + "image_url", + "jobs", + "model", + "name" + ] + }, + "PublicStatsPrinting": { + "type": "object", + "properties": { + "hours_all_time": { + "type": "number", + "format": "double", "readOnly": true }, - "currency": { - "type": "string", + "hours_this_month": { + "type": "number", + "format": "double", "readOnly": true }, - "availability": { + "busiest_printer": { "allOf": [ { - "$ref": "#/components/schemas/AvailabilityEnum" + "$ref": "#/components/schemas/PublicStatsBusiestPrinter" } ], - "readOnly": true - }, - "registration_requires_approval": { - "type": "boolean", - "readOnly": true + "readOnly": true, + "nullable": true }, - "effective_registration_cutoff_at": { - "type": "string", - "format": "date-time", - "nullable": true, + "per_printer": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicStatsPrinter" + }, "readOnly": true }, - "registration_open": { - "type": "boolean", + "grams_all_time": { + "type": "number", + "format": "double", "readOnly": true }, - "image_url": { - "type": "string", - "format": "uri", - "nullable": true, + "by_brand": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicStatsBrand" + }, "readOnly": true }, - "status": { + "jobs": { "allOf": [ { - "$ref": "#/components/schemas/StatusE90Enum" + "$ref": "#/components/schemas/PublicStatsJobs" } ], "readOnly": true }, - "organizers": { + "filament_trend": { "type": "array", "items": { - "$ref": "#/components/schemas/EventOrganizerSummary" - }, - "readOnly": true - }, - "series": { - "type": "object", - "nullable": true, - "properties": { - "public_token": { - "type": "string", - "format": "uuid" - }, - "title": { - "type": "string" - } + "$ref": "#/components/schemas/PublicStatsFilamentTrend" }, "readOnly": true } }, "required": [ - "availability", - "capacity", - "category", - "currency", - "custom_form", - "description", - "effective_registration_cutoff_at", - "ends_at", - "image_url", - "location", - "location_kind", - "organizers", - "payment_amount", - "promo_codes_enabled", - "public_token", - "registration_audience", - "registration_open", - "registration_requires_approval", - "series", - "starts_at", - "status", - "tags", - "ticket_selection_required", - "ticket_types", - "title" + "busiest_printer", + "by_brand", + "filament_trend", + "grams_all_time", + "hours_all_time", + "hours_this_month", + "jobs", + "per_printer" ] }, - "PublicEventRegistrationInput": { + "PublicStatsQueue": { "type": "object", "properties": { - "custom_answers": { - "writeOnly": true, - "nullable": true - }, - "phone": { - "type": "string", - "writeOnly": true, - "maxLength": 64 - }, - "email": { - "writeOnly": true, - "oneOf": [ - { - "type": "string", - "format": "email" - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "invite_code": { - "type": "string", - "writeOnly": true, - "maxLength": 128 + "pending": { + "type": "integer", + "readOnly": true }, - "ticket_type_id": { + "accepted": { "type": "integer", - "minimum": 1, - "writeOnly": true + "readOnly": true }, - "promo_code": { - "type": "string", - "writeOnly": true, - "maxLength": 128 - } - } - }, - "PublicEventRegistrationResponse": { - "type": "object", - "properties": { - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicEventRegistrationResponseStatusEnum" - } - ], + "printing": { + "type": "integer", "readOnly": true } }, "required": [ - "status" + "accepted", + "pending", + "printing" ] }, - "PublicEventRegistrationResponseStatusEnum": { - "enum": [ - "pending_approval", - "registered", - "waitlisted" - ], - "type": "string", - "description": "* `pending_approval` - pending_approval\n* `registered` - registered\n* `waitlisted` - waitlisted" - }, - "PublicImageAttachRequest": { + "PublicStatsRecentlyAdded": { "type": "object", "properties": { - "object_key": { + "name": { "type": "string", - "maxLength": 300 + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ - "object_key" + "created_at", + "name" ] }, - "PublicImageUploadRequest": { + "PublicStatsStatusCounts": { "type": "object", "properties": { - "content_type": { - "type": "string" + "pending": { + "type": "integer", + "readOnly": true }, - "filename": { - "type": "string", - "maxLength": 255 + "accepted": { + "type": "integer", + "readOnly": true + }, + "printing": { + "type": "integer", + "readOnly": true + }, + "completed": { + "type": "integer", + "readOnly": true + }, + "collected": { + "type": "integer", + "readOnly": true + }, + "failed": { + "type": "integer", + "readOnly": true + }, + "rejected": { + "type": "integer", + "readOnly": true } }, "required": [ - "content_type", - "filename" + "accepted", + "collected", + "completed", + "failed", + "pending", + "printing", + "rejected" ] }, - "PublicImageUploadResponse": { + "PublicStatsToolsOut": { "type": "object", "properties": { - "object_key": { - "type": "string" - }, - "url": { + "name": { "type": "string", - "format": "uri" - }, - "fields": { - "type": "object", - "additionalProperties": {} - }, - "method": { - "type": "string" + "readOnly": true }, - "headers": { - "type": "object", - "additionalProperties": {} + "quantity_out": { + "type": "integer", + "readOnly": true } }, "required": [ - "object_key", - "url" + "name", + "quantity_out" ] }, - "PublicInventoryError": { + "PublicToolCheckout": { "type": "object", "properties": { - "detail": { - "type": "string" + "kit_checklist": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitCount" + }, + "maxItems": 3550 }, - "module": { - "type": "string" + "payload": { + "type": "string", + "maxLength": 64 }, - "feature": { - "type": "string" + "evidence_id": { + "type": "integer" }, - "code": { + "remark": { "type": "string" } - } - }, - "PublicLandingPage6a0Enum": { - "enum": [ - "auto", - "inventory", - "events", - "machines", - "bookings", - "membership" - ], - "type": "string", - "description": "* `auto` - Auto\n* `inventory` - Inventory\n* `events` - Events\n* `machines` - Machines\n* `bookings` - Bookings\n* `membership` - Membership" + }, + "required": [ + "evidence_id", + "payload" + ] }, - "PublicMachine": { + "PublicToolEvidenceUrlRequest": { "type": "object", - "description": "Allowlist-only public projection; never subclass the staff serializer.", "properties": { - "name": { - "type": "string", - "readOnly": true - }, - "machine_type": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicMachineType" - } - ], - "readOnly": true - }, - "image_url": { - "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true + "evidence_type": { + "$ref": "#/components/schemas/EvidenceType740Enum" }, - "status": { - "type": "string", - "readOnly": true + "content_type": { + "type": "string" }, - "usage_hours": { - "type": "string", - "format": "decimal", - "example": "12.50", - "readOnly": true + "size_bytes": { + "type": "integer", + "minimum": 0, + "nullable": true } }, "required": [ - "image_url", - "machine_type", - "name", - "status", - "usage_hours" + "content_type", + "evidence_type" ] }, - "PublicMachineDetail": { + "PublicToolLoan": { "type": "object", - "description": "Allowlist-only public projection; never subclass the staff serializer.", "properties": { - "name": { - "type": "string", - "readOnly": true - }, - "machine_type": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "icon": { - "type": "string" - } - }, + "has_kit_checklist": { + "type": "boolean", "readOnly": true }, - "image_url": { + "public_token": { "type": "string", - "format": "uri", - "nullable": true, + "format": "uuid", "readOnly": true }, "status": { "type": "string", "readOnly": true }, - "usage_hours": { - "type": "string", - "format": "decimal", - "readOnly": true - }, - "id": { - "type": "integer", - "readOnly": true - }, - "status_since": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "training_required": { - "type": "boolean", - "nullable": true, - "readOnly": true - }, - "queue": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicQueueEstimate" - } - ], - "nullable": true, + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicToolLoanItem" + }, "readOnly": true } }, "required": [ - "id", - "image_url", - "machine_type", - "name", - "queue", - "status", - "status_since", - "training_required", - "usage_hours" + "has_kit_checklist", + "items", + "public_token", + "status" ] }, - "PublicMachineServiceSubmit": { + "PublicToolLoanItem": { "type": "object", "properties": { - "certification_override_reason": { - "type": "string", - "writeOnly": true, - "maxLength": 1000 - }, - "website": { - "type": "string" - }, - "machine_id": { - "type": "integer", - "minimum": 1 - }, - "title": { - "type": "string", - "maxLength": 200 - }, - "description": { + "product_name": { "type": "string" }, - "source_link": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "capability_payload": {} + "quantity": { + "type": "integer" + } }, "required": [ - "machine_id", - "title" + "product_name", + "quantity" ] }, - "PublicMachineServiceSubmitResponse": { + "PublicToolScan": { "type": "object", "properties": { - "public_token": { + "kit_checklist": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitCount" + }, + "maxItems": 3550 + }, + "payload": { "type": "string", - "format": "uuid" + "maxLength": 64 }, - "status": { + "evidence_id": { + "type": "integer" + }, + "remark": { "type": "string" + }, + "report_problem": { + "type": "boolean", + "default": false + }, + "problem_note": { + "type": "string", + "default": "" } }, "required": [ - "public_token", - "status" + "evidence_id", + "payload", + "remark" ] }, - "PublicMachineType": { + "PublicTypeIntake": { "type": "object", "properties": { - "name": { - "type": "string", - "readOnly": true + "materials": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PublicMaterial" + } }, - "icon": { - "type": "string", - "readOnly": true - } + "intake_schema": {} }, "required": [ - "icon", - "name" + "intake_schema", + "materials" ] }, - "PublicMakerspace": { + "PushDevice": { "type": "object", "properties": { - "name": { - "type": "string", - "readOnly": true + "id": { + "type": "integer" }, - "public_code": { - "type": "string", - "readOnly": true + "provider": { + "type": "string" }, - "slug": { - "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "environment": { + "type": "string" }, - "location": { - "type": "string", - "readOnly": true + "makerspace_id": { + "type": "integer" }, - "map_url": { - "type": "string", - "readOnly": true + "active": { + "type": "boolean" }, - "logo_url": { + "created_at": { "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true + "format": "date-time" }, - "cover_image_url": { + "updated_at": { "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true + "format": "date-time" } }, "required": [ - "cover_image_url", - "location", - "logo_url", - "map_url", - "name", - "public_code", - "slug" + "active", + "created_at", + "environment", + "id", + "makerspace_id", + "provider", + "updated_at" ] }, - "PublicMaterial": { + "PushDeviceRegistration": { "type": "object", "properties": { - "id": { - "type": "integer", - "nullable": true + "token": { + "type": "string", + "writeOnly": true, + "maxLength": 4096, + "pattern": "^[A-Za-z0-9_:.-]{20,4096}$" }, - "name": { - "type": "string" + "provider": { + "$ref": "#/components/schemas/PushDeviceRegistrationProviderEnum" }, - "colours": { - "type": "array", - "items": { - "type": "string" - } + "environment": { + "$ref": "#/components/schemas/EnvironmentEnum" } }, "required": [ - "colours", - "id", - "name" + "environment", + "provider", + "token" ] }, - "PublicOpeningInterval": { + "PushDeviceRegistrationProviderEnum": { + "enum": [ + "fcm", + "apns" + ], + "type": "string", + "description": "* `fcm` - FCM\n* `apns` - APNs" + }, + "QrCode": { "type": "object", "properties": { - "starts_at": { + "id": { + "type": "integer", + "readOnly": true + }, + "makerspace": { + "type": "integer" + }, + "payload": { + "type": "string", + "readOnly": true + }, + "target_type": { + "$ref": "#/components/schemas/QrCodeTargetTypeEnum" + }, + "target_id": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/Status37fEnum" + } + ], + "readOnly": true, + "default": "active" + }, + "created_at": { "type": "string", "format": "date-time", "readOnly": true }, - "ends_at": { + "updated_at": { "type": "string", "format": "date-time", "readOnly": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true } }, "required": [ - "ends_at", - "starts_at" + "created_at", + "id", + "makerspace", + "payload", + "revoked_at", + "status", + "target_id", + "target_type", + "updated_at" ] }, - "PublicOrganization": { + "QrCodeTargetTypeEnum": { + "enum": [ + "box", + "product", + "asset", + "member_card" + ], + "type": "string", + "description": "* `box` - Box\n* `product` - Product\n* `asset` - Asset\n* `member_card` - Member card" + }, + "QrHistoryItem": { "type": "object", "properties": { - "slug": { - "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "id": { + "type": "string" }, - "name": { - "type": "string", - "readOnly": true + "source": { + "type": "string" }, - "description": { - "type": "string", - "readOnly": true + "context": { + "type": "string" }, - "website": { - "type": "string", - "format": "uri", - "readOnly": true + "actor": { + "type": "integer", + "nullable": true }, - "logo_url": { + "created_at": { "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true - }, - "catalogue_links": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "readOnly": true + "format": "date-time" } }, "required": [ - "catalogue_links", - "description", - "logo_url", - "name", - "slug", - "website" + "actor", + "context", + "created_at", + "id", + "source" ] }, - "PublicOrganizationEvent": { + "QrPrintBatch": { "type": "object", "properties": { - "registration_audience": { - "allOf": [ - { - "$ref": "#/components/schemas/EventDefaultRegistrationAudienceEnum" - } - ], - "readOnly": true - }, - "category": { - "type": "object", - "nullable": true, - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "slug": { - "type": "string" - } - }, - "readOnly": true - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true - }, - "ticket_types": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "amount": { - "type": "string" - }, - "audience": { - "type": "string" - }, - "spots_left": { - "type": "integer", - "nullable": true - } - } - }, - "readOnly": true - }, - "promo_codes_enabled": { - "type": "boolean", - "readOnly": true - }, - "ticket_selection_required": { - "type": "boolean", + "id": { + "type": "integer", "readOnly": true }, - "public_token": { - "type": "string", - "format": "uuid", - "readOnly": true + "makerspace": { + "type": "integer" }, "title": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "description": { - "type": "string", - "readOnly": true + "status": { + "$ref": "#/components/schemas/QrPrintBatchStatusEnum" }, - "starts_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "created_by": { + "type": "integer", + "nullable": true }, - "ends_at": { + "created_at": { "type": "string", "format": "date-time", "readOnly": true }, - "location": { + "printed_at": { "type": "string", - "readOnly": true - }, - "location_kind": { - "allOf": [ - { - "$ref": "#/components/schemas/LocationKindEnum" - } - ], - "readOnly": true - }, - "custom_form": { - "readOnly": true, + "format": "date-time", "nullable": true - }, - "capacity": { + } + }, + "required": [ + "created_at", + "id", + "makerspace", + "title" + ] + }, + "QrPrintBatchCreate": { + "type": "object", + "properties": { + "title": { + "type": "string" + } + }, + "required": [ + "title" + ] + }, + "QrPrintBatchDetail": { + "type": "object", + "properties": { + "id": { "type": "integer", - "minimum": 0, "readOnly": true }, - "payment_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true + "makerspace": { + "type": "integer" }, - "currency": { + "title": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "availability": { - "allOf": [ - { - "$ref": "#/components/schemas/AvailabilityEnum" - } - ], - "readOnly": true + "status": { + "$ref": "#/components/schemas/QrPrintBatchStatusEnum" }, - "registration_requires_approval": { - "type": "boolean", - "readOnly": true + "created_by": { + "type": "integer", + "nullable": true }, - "effective_registration_cutoff_at": { + "created_at": { "type": "string", "format": "date-time", - "nullable": true, - "readOnly": true - }, - "registration_open": { - "type": "boolean", "readOnly": true }, - "image_url": { + "printed_at": { "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusE90Enum" - } - ], - "readOnly": true + "format": "date-time", + "nullable": true }, - "organizers": { + "items": { "type": "array", "items": { - "$ref": "#/components/schemas/EventOrganizerSummary" - }, - "readOnly": true - }, - "series": { - "type": "object", - "nullable": true, - "properties": { - "public_token": { - "type": "string", - "format": "uuid" - }, - "title": { - "type": "string" - } + "$ref": "#/components/schemas/QrPrintBatchItem" }, "readOnly": true - }, - "host": { - "allOf": [ - { - "$ref": "#/components/schemas/OrganizationEventHost" - } - ], - "readOnly": true } }, "required": [ - "availability", - "capacity", - "category", - "currency", - "custom_form", - "description", - "effective_registration_cutoff_at", - "ends_at", - "host", - "image_url", - "location", - "location_kind", - "organizers", - "payment_amount", - "promo_codes_enabled", - "public_token", - "registration_audience", - "registration_open", - "registration_requires_approval", - "series", - "starts_at", - "status", - "tags", - "ticket_selection_required", - "ticket_types", + "created_at", + "id", + "items", + "makerspace", "title" ] }, - "PublicOrganizationEventList": { + "QrPrintBatchItem": { "type": "object", "properties": { - "count": { - "type": "integer" + "id": { + "type": "integer", + "readOnly": true }, - "next": { + "qr_code": { + "allOf": [ + { + "$ref": "#/components/schemas/QrCode" + } + ], + "readOnly": true + }, + "label_text": { "type": "string", - "nullable": true + "maxLength": 255 }, - "previous": { + "target_type": { "type": "string", - "nullable": true + "maxLength": 20 }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicOrganizationEvent" - } + "target_id": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "sort_order": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 } }, "required": [ - "count", - "next", - "previous", - "results" + "id", + "label_text", + "qr_code", + "target_id", + "target_type" ] }, - "PublicPrintStatusLookupPolicyEnum": { - "enum": [ - "token_only", - "email_unverified" - ], - "type": "string", - "description": "* `token_only` - Token only\n* `email_unverified` - Email unverified" - }, - "PublicPrinterPool": { + "QrPrintBatchItemCreate": { "type": "object", "properties": { - "id": { + "qr_code_id": { "type": "integer" }, - "material": { + "label_text": { "type": "string" }, - "color": { - "type": "string" + "sort_order": { + "type": "integer", + "minimum": 0 } }, "required": [ - "color", - "id", - "material" + "qr_code_id" ] }, - "PublicPrinterQueue": { + "QrPrintBatchItemResult": { "type": "object", "properties": { - "machine_type_id": { - "type": "integer" - }, "id": { "type": "integer" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" } }, "required": [ - "description", - "id", - "machine_type_id", - "name" + "id" ] }, - "PublicPrinterStatus": { + "QrPrintBatchStatusEnum": { + "enum": [ + "draft", + "printed", + "archived" + ], + "type": "string", + "description": "* `draft` - Draft\n* `printed` - Printed\n* `archived` - Archived" + }, + "QrRebindResult": { "type": "object", - "description": "The token endpoint's complete field allowlist. Do not add contact fields.", "properties": { - "public_token": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string" - }, - "title": { - "type": "string" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "accepted_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "started_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "estimated_minutes": { - "type": "integer" - }, - "queue_position": { - "type": "integer", - "nullable": true, - "readOnly": true - }, - "queue_approved_ahead": { - "type": "integer", - "nullable": true, - "readOnly": true + "qr": { + "$ref": "#/components/schemas/QrCode" }, - "queue_awaiting_review_ahead": { - "type": "integer", - "nullable": true, - "readOnly": true + "target": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "accepted_at", - "completed_at", - "created_at", - "estimated_minutes", - "public_token", - "queue_approved_ahead", - "queue_awaiting_review_ahead", - "queue_position", - "started_at", - "status", - "title" + "qr", + "target" ] }, - "PublicPrinterSubmit": { + "QrRebindTarget": { "type": "object", "properties": { - "certification_override_reason": { - "type": "string", - "writeOnly": true, - "maxLength": 1000 + "target_type": { + "$ref": "#/components/schemas/QrRebindTargetTargetTypeEnum" }, - "website": { - "type": "string" + "target_id": { + "type": "integer" }, - "queue_id": { + "destination_makerspace_id": { "type": "integer", "nullable": true }, - "title": { - "type": "string", - "maxLength": 200 - }, - "project_brief": { - "type": "string" - }, - "preferred_settings": { - "nullable": true - }, - "material": { - "type": "string", - "maxLength": 100 - }, - "color": { - "type": "string", - "maxLength": 100 - }, - "consumable_pool_id": { + "destination_product_id": { "type": "integer", "nullable": true }, - "estimated_filament_grams": { + "new_name": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,6}(?:\\.\\d{0,2})?$", - "nullable": true - }, - "quantity": { - "type": "integer", - "minimum": 1, - "default": 1 - }, - "source_link": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "file_ids": { - "type": "array", - "items": { - "type": "integer" - } + "maxLength": 100 } }, "required": [ - "title" + "target_id", + "target_type" ] }, - "PublicPrinterSubmitResponse": { + "QrRebindTargetTargetTypeEnum": { + "enum": [ + "product", + "asset" + ], + "type": "string", + "description": "* `product` - product\n* `asset` - asset" + }, + "QrResolve": { "type": "object", "properties": { - "public_token": { - "type": "string", - "format": "uuid" - }, - "status": { + "payload": { "type": "string" } }, "required": [ - "public_token", - "status" + "payload" ] }, - "PublicPrinterUpload": { + "QrResolveResult": { "type": "object", "properties": { - "queue_id": { - "type": "integer", - "nullable": true - }, - "kind": { - "$ref": "#/components/schemas/PublicPrinterUploadKindEnum" + "qr": { + "$ref": "#/components/schemas/QrCode" }, - "filename": { - "type": "string", - "maxLength": 255 + "target": { + "type": "object", + "additionalProperties": {} }, - "content_type": { - "type": "string", - "maxLength": 128 + "allowed_actions": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "filename", - "kind" + "allowed_actions", + "qr", + "target" ] }, - "PublicPrinterUploadKindEnum": { - "enum": [ - "stl", - "screenshot" - ], - "type": "string", - "description": "* `stl` - stl\n* `screenshot` - screenshot" - }, - "PublicProduct": { + "QrScan": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "name": { - "type": "string", - "readOnly": true + "payload": { + "type": "string" }, - "description": { - "type": "string", - "readOnly": true + "context": { + "$ref": "#/components/schemas/ContextEnum" }, - "category_id": { + "request_id": { "type": "integer", - "readOnly": true, - "nullable": true - }, - "category_name": { - "type": "string", - "readOnly": true, "nullable": true + } + }, + "required": [ + "context", + "payload" + ] + }, + "QrScanResult": { + "type": "object", + "properties": { + "qr": { + "$ref": "#/components/schemas/QrCode" }, - "category_slug": { - "type": "string", - "readOnly": true, - "nullable": true - }, - "borrowing_terms": { - "type": "string", - "readOnly": true - }, - "availability": { + "target": { "type": "object", - "nullable": true, - "properties": { - "mode": { - "type": "string", - "enum": [ - "status_only", - "exact_count" - ] - }, - "label": { - "type": "string", - "enum": [ - "Available", - "Limited", - "Unavailable" - ] - }, - "count": { - "type": "integer", - "minimum": 0 - } - }, - "required": [ - "mode" - ], - "readOnly": true + "additionalProperties": {} }, - "image_url": { - "type": "string", - "format": "uri", - "nullable": true, - "readOnly": true + "scan_id": { + "type": "integer" } }, "required": [ - "availability", - "borrowing_terms", - "category_id", - "category_name", - "category_slug", - "description", - "id", - "image_url", - "name" + "qr", + "scan_id", + "target" ] }, - "PublicQueueEstimate": { + "QrScansReport": { "type": "object", "properties": { - "length": { - "type": "integer", - "minimum": 0 + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "eta_minutes": { - "type": "integer", - "minimum": 0, - "nullable": true + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QrScansReportRow" + } } }, "required": [ - "eta_minutes", - "length" + "rows", + "typed_rows" ] }, - "PublicRequestItemStatus": { + "QrScansReportRow": { "type": "object", "properties": { - "product_name": { + "makerspace_id": { + "type": "integer" + }, + "period": { "type": "string", - "readOnly": true + "format": "date" }, - "requested_quantity": { + "context": { + "type": "string" + }, + "target_type": { + "type": "string" + }, + "target_id": { "type": "integer", - "readOnly": true + "nullable": true, + "description": "Member-card identifiers are suppressed." + }, + "hour_of_week": { + "type": "integer", + "maximum": 167, + "minimum": 0 + }, + "count": { + "type": "integer" } }, "required": [ - "product_name", - "requested_quantity" + "context", + "count", + "hour_of_week", + "period", + "target_id", + "target_type" ] }, - "PublicRequestStatus": { + "QuantityChainSummary": { "type": "object", "properties": { - "status": { - "type": "string", - "readOnly": true + "loan_count": { + "type": "integer" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "direct_loan_count": { + "type": "integer" }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicRequestItemStatus" - }, - "readOnly": true + "issued_quantity": { + "type": "integer" + }, + "returned_quantity": { + "type": "integer" + }, + "damaged_quantity": { + "type": "integer" + }, + "missing_quantity": { + "type": "integer" + }, + "active_quantity": { + "type": "integer" } }, "required": [ - "created_at", - "items", - "status" + "active_quantity", + "damaged_quantity", + "direct_loan_count", + "issued_quantity", + "loan_count", + "missing_quantity", + "returned_quantity" ] }, - "PublicSpaceAvailability": { + "RailEnum": { + "enum": [ + "online", + "offline", + "unallocated" + ], + "type": "string", + "description": "* `online` - online\n* `offline` - offline\n* `unallocated` - unallocated" + }, + "RateLimitTierEnum": { + "enum": [ + "public", + "standard", + "trusted" + ], + "type": "string", + "description": "* `public` - Public\n* `standard` - Standard\n* `trusted` - Trusted" + }, + "RazorpayMobilePaymentIntentResponse": { "type": "object", "properties": { - "public_token": { - "type": "string", - "format": "uuid", - "readOnly": true + "vendor": { + "$ref": "#/components/schemas/RazorpayMobilePaymentIntentResponseVendorEnum" }, - "starts_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "payment_id": { + "type": "integer" }, - "ends_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "key_id": { + "type": "string" }, - "timezone": { - "type": "string", - "readOnly": true + "order_id": { + "type": "string" }, - "opening_intervals": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicOpeningInterval" - }, - "readOnly": true, - "description": "Open windows in UTC, accounting for makerspace hours and dated exceptions." + "amount_minor": { + "type": "integer" }, - "availability": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicAvailabilityInterval" - }, - "readOnly": true, - "nullable": true + "currency": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": [ + "amount_minor", + "currency", + "description", + "key_id", + "name", + "order_id", + "payment_id", + "vendor" + ] + }, + "RazorpayMobilePaymentIntentResponseVendorEnum": { + "enum": [ + "razorpay" + ], + "type": "string", + "description": "* `razorpay` - razorpay" + }, + "ReadAll": { + "type": "object", + "properties": { + "updated": { + "type": "integer" } }, "required": [ - "availability", - "ends_at", - "opening_intervals", - "public_token", - "starts_at", - "timezone" + "updated" ] }, - "PublicStats": { + "Readiness": { "type": "object", "properties": { - "machines": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicStatsMachines" - } - ], - "readOnly": true, - "nullable": true - }, - "printing": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicStatsPrinting" - } - ], - "readOnly": true, - "nullable": true + "status": { + "type": "string" }, - "hardware": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicStatsHardware" - } - ], - "readOnly": true + "database": { + "type": "string" }, - "current_loans": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicStatsCurrentLoan" - }, - "readOnly": true + "archive_custody": { + "$ref": "#/components/schemas/ArchiveCustodyReadiness" } }, "required": [ - "current_loans", - "hardware", - "machines", - "printing" + "archive_custody", + "database", + "status" ] }, - "PublicStatsBrand": { + "ReadinessItem": { "type": "object", "properties": { - "brand": { - "type": "string", - "readOnly": true + "ready": { + "type": "boolean" }, - "grams": { - "type": "number", - "format": "double", - "readOnly": true + "hint": { + "type": "string" } }, "required": [ - "brand", - "grams" + "hint", + "ready" ] }, - "PublicStatsBusiestPrinter": { + "Reason870Enum": { + "enum": [ + "bounce", + "complaint" + ], + "type": "string", + "description": "* `bounce` - Hard bounce\n* `complaint` - Spam complaint" + }, + "Reauthentication": { "type": "object", "properties": { - "name": { + "password": { "type": "string", - "readOnly": true + "writeOnly": true, + "default": "", + "maxLength": 1024 }, - "model": { + "code": { "type": "string", - "readOnly": true - }, - "hours": { - "type": "number", - "format": "double", - "readOnly": true - }, - "completed": { - "type": "integer", - "readOnly": true - }, - "image_url": { + "writeOnly": true, + "default": "", + "maxLength": 6 + } + } + }, + "ReauthenticationEmailConfirm": { + "type": "object", + "properties": { + "token": { "type": "string", - "format": "uri", - "readOnly": true, - "nullable": true + "writeOnly": true, + "maxLength": 200 } }, "required": [ - "completed", - "hours", - "image_url", - "model", - "name" + "token" ] }, - "PublicStatsCurrentLoan": { + "ReauthenticationEmailConfirmed": { "type": "object", "properties": { - "item_name": { - "type": "string", - "readOnly": true - }, - "holder_name": { - "type": "string", - "readOnly": true - }, - "due": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true - }, - "since": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "confirmed": { + "type": "boolean" } }, "required": [ - "due", - "holder_name", - "item_name", - "since" + "confirmed" ] }, - "PublicStatsFilamentTrend": { + "ReauthenticationEmailSent": { "type": "object", "properties": { - "period": { - "type": "string", - "readOnly": true - }, - "grams": { - "type": "number", - "format": "double", - "readOnly": true + "sent": { + "type": "boolean" } }, "required": [ - "grams", - "period" + "sent" ] }, - "PublicStatsHardware": { + "ReauthenticationFactor": { "type": "object", "properties": { - "most_popular": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicStatsPopularHardware" - }, - "readOnly": true - }, - "tools_out": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicStatsToolsOut" - }, - "readOnly": true - }, - "library": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicStatsLibrary" - } - ], - "readOnly": true + "token": { + "type": "string", + "writeOnly": true, + "maxLength": 200 }, - "recently_added": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PublicStatsRecentlyAdded" - }, - "readOnly": true + "code": { + "type": "string", + "writeOnly": true, + "maxLength": 6, + "pattern": "^[0-9]{6}$" } }, "required": [ - "library", - "most_popular", - "recently_added", - "tools_out" + "code", + "token" ] }, - "PublicStatsJobs": { + "ReauthenticationPhoneStarted": { "type": "object", "properties": { - "completed": { - "type": "integer", - "readOnly": true - }, - "status_counts": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicStatsStatusCounts" - } - ], - "readOnly": true + "sent": { + "type": "boolean" }, - "queue": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicStatsQueue" - } - ], - "readOnly": true + "token": { + "type": "string" } }, "required": [ - "completed", - "queue", - "status_counts" + "sent", + "token" ] }, - "PublicStatsLibrary": { + "ReauthenticationResult": { "type": "object", "properties": { - "currently_out_count": { - "type": "integer", - "readOnly": true + "verified": { + "type": "boolean" }, - "library_size": { - "type": "integer", - "readOnly": true + "expires_in": { + "type": "integer" }, - "available_count": { - "type": "integer", - "readOnly": true + "mfa_required": { + "type": "boolean" + }, + "factor_required": { + "$ref": "#/components/schemas/FactorRequiredEnum" + }, + "factor_token": { + "type": "string" } }, "required": [ - "available_count", - "currently_out_count", - "library_size" + "expires_in", + "verified" ] }, - "PublicStatsMachines": { + "ReauthenticationSocialComplete": { "type": "object", "properties": { - "usage_hours": { - "type": "number", - "format": "double", - "readOnly": true + "state": { + "type": "string", + "writeOnly": true, + "maxLength": 200 + }, + "nonce": { + "type": "string", + "writeOnly": true, + "maxLength": 200 + }, + "id_token": { + "type": "string", + "writeOnly": true, + "default": "", + "maxLength": 16384 + }, + "code": { + "type": "string", + "writeOnly": true, + "default": "", + "maxLength": 2048 + }, + "totp": { + "type": "string", + "writeOnly": true, + "default": "", + "maxLength": 6 } }, "required": [ - "usage_hours" + "nonce", + "state" ] }, - "PublicStatsPopularHardware": { + "ReauthenticationSocialStart": { "type": "object", "properties": { - "name": { + "provider": { "type": "string", - "readOnly": true - }, - "times_lent": { - "type": "integer", - "readOnly": true + "maxLength": 64 }, - "total_quantity_lent": { - "type": "integer", - "readOnly": true + "redirect_uri": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "default": "", + "maxLength": 2048 + }, + { + "type": "string", + "maxLength": 0 + } + ] } }, "required": [ - "name", - "times_lent", - "total_quantity_lent" + "provider" ] }, - "PublicStatsPrinter": { + "ReauthenticationSocialStarted": { "type": "object", "properties": { - "name": { - "type": "string", - "readOnly": true - }, - "model": { + "authorization_url": { "type": "string", - "readOnly": true - }, - "jobs": { - "type": "integer", - "readOnly": true + "format": "uri" }, - "hours": { - "type": "number", - "format": "double", - "readOnly": true + "state": { + "type": "string" }, - "grams": { - "type": "number", - "format": "double", - "readOnly": true + "nonce": { + "type": "string" }, - "image_url": { - "type": "string", - "format": "uri", - "readOnly": true, - "nullable": true + "expires_in": { + "type": "integer" } }, "required": [ - "grams", - "hours", - "image_url", - "jobs", - "model", - "name" + "authorization_url", + "expires_in", + "nonce", + "state" ] }, - "PublicStatsPrinting": { + "ReauthenticationStatus": { "type": "object", "properties": { - "hours_all_time": { - "type": "number", - "format": "double", - "readOnly": true + "verified": { + "type": "boolean" }, - "hours_this_month": { - "type": "number", - "format": "double", - "readOnly": true + "password_available": { + "type": "boolean" }, - "busiest_printer": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicStatsBusiestPrinter" - } - ], - "readOnly": true, - "nullable": true + "mfa_enabled": { + "type": "boolean" }, - "per_printer": { + "providers": { "type": "array", "items": { - "$ref": "#/components/schemas/PublicStatsPrinter" - }, - "readOnly": true + "type": "string" + } + } + }, + "required": [ + "mfa_enabled", + "password_available", + "providers", + "verified" + ] + }, + "ReceiptEnvelope": { + "type": "object", + "properties": { + "payload": { + "type": "object", + "additionalProperties": {} }, - "grams_all_time": { - "type": "number", - "format": "double", - "readOnly": true + "signer_fingerprint": { + "type": "string", + "maxLength": 64 }, - "by_brand": { + "signature": { + "type": "string", + "maxLength": 88 + } + }, + "required": [ + "payload", + "signature", + "signer_fingerprint" + ] + }, + "RecentlyAddedReport": { + "type": "object", + "properties": { + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/PublicStatsBrand" - }, - "readOnly": true - }, - "jobs": { - "allOf": [ - { - "$ref": "#/components/schemas/PublicStatsJobs" - } - ], - "readOnly": true + "type": "array", + "items": {} + } }, - "filament_trend": { + "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/PublicStatsFilamentTrend" - }, - "readOnly": true + "$ref": "#/components/schemas/RecentlyAddedReportRow" + } } }, "required": [ - "busiest_printer", - "by_brand", - "filament_trend", - "grams_all_time", - "hours_all_time", - "hours_this_month", - "jobs", - "per_printer" + "rows", + "typed_rows" ] }, - "PublicStatsQueue": { + "RecentlyAddedReportRow": { "type": "object", "properties": { - "pending": { - "type": "integer", - "readOnly": true + "makerspace_id": { + "type": "integer" }, - "accepted": { - "type": "integer", - "readOnly": true + "product_name": { + "type": "string" }, - "printing": { - "type": "integer", - "readOnly": true + "created_at": { + "type": "string", + "format": "date-time" + }, + "total_quantity": { + "type": "integer" } }, "required": [ - "accepted", - "pending", - "printing" + "created_at", + "product_name", + "total_quantity" ] }, - "PublicStatsRecentlyAdded": { + "RecipeEnum": { + "enum": [ + "free_open", + "free_vouch", + "paid_approval", + "paid_invite_only", + "closed" + ], + "type": "string", + "description": "* `free_open` - free_open\n* `free_vouch` - free_vouch\n* `paid_approval` - paid_approval\n* `paid_invite_only` - paid_invite_only\n* `closed` - closed" + }, + "RecipientActionsEnum": { + "enum": [ + "accept_request", + "assign_box", + "collect_service_request", + "edit_inventory", + "issue_direct_loan", + "issue_request", + "manage_bookings", + "manage_devices", + "manage_events", + "manage_machines", + "manage_makerspace", + "manage_member_cards", + "manage_members", + "manage_printing", + "manage_qr", + "record_payment", + "reject_request", + "return_request", + "scan_member_cards", + "upload_evidence", + "view_audit", + "view_finance", + "view_inventory" + ], + "type": "string", + "description": "* `accept_request` - accept_request\n* `assign_box` - assign_box\n* `collect_service_request` - collect_service_request\n* `edit_inventory` - edit_inventory\n* `issue_direct_loan` - issue_direct_loan\n* `issue_request` - issue_request\n* `manage_bookings` - manage_bookings\n* `manage_devices` - manage_devices\n* `manage_events` - manage_events\n* `manage_machines` - manage_machines\n* `manage_makerspace` - manage_makerspace\n* `manage_member_cards` - manage_member_cards\n* `manage_members` - manage_members\n* `manage_printing` - manage_printing\n* `manage_qr` - manage_qr\n* `record_payment` - record_payment\n* `reject_request` - reject_request\n* `return_request` - return_request\n* `scan_member_cards` - scan_member_cards\n* `upload_evidence` - upload_evidence\n* `view_audit` - view_audit\n* `view_finance` - view_finance\n* `view_inventory` - view_inventory" + }, + "RecipientFeature": { "type": "object", "properties": { - "name": { - "type": "string", - "readOnly": true + "key": { + "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "events": { + "type": "array", + "items": { + "type": "string" + } } }, "required": [ - "created_at", - "name" + "events", + "key" ] }, - "PublicStatsStatusCounts": { + "RecipientMember": { "type": "object", "properties": { - "pending": { - "type": "integer", - "readOnly": true - }, - "accepted": { - "type": "integer", - "readOnly": true - }, - "printing": { - "type": "integer", - "readOnly": true - }, - "completed": { - "type": "integer", - "readOnly": true - }, - "collected": { - "type": "integer", - "readOnly": true + "id": { + "type": "integer" }, - "failed": { - "type": "integer", - "readOnly": true + "username": { + "type": "string" }, - "rejected": { - "type": "integer", - "readOnly": true + "email": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "maxLength": 0 + } + ] } }, "required": [ - "accepted", - "collected", - "completed", - "failed", - "pending", - "printing", - "rejected" + "email", + "id", + "username" ] }, - "PublicStatsToolsOut": { + "RecipientRole": { "type": "object", "properties": { + "id": { + "type": "integer" + }, "name": { - "type": "string", - "readOnly": true + "type": "string" }, - "quantity_out": { - "type": "integer", - "readOnly": true + "slug": { + "type": "string" } }, "required": [ + "id", "name", - "quantity_out" + "slug" ] }, - "PublicToolCheckout": { + "RecipientRule": { "type": "object", "properties": { - "kit_checklist": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 + "kind": { + "$ref": "#/components/schemas/KindE56Enum" }, - "payload": { - "type": "string", - "maxLength": 64 + "role_id": { + "type": "integer", + "nullable": true }, - "evidence_id": { - "type": "integer" + "user_id": { + "type": "integer", + "nullable": true }, - "remark": { - "type": "string" + "scope": { + "$ref": "#/components/schemas/RecipientScope" } }, "required": [ - "evidence_id", - "payload" + "kind" ] }, - "PublicToolEvidenceUrlRequest": { + "RecipientRuleOutput": { "type": "object", "properties": { - "evidence_type": { - "$ref": "#/components/schemas/EvidenceType740Enum" + "kind": { + "$ref": "#/components/schemas/KindE56Enum" }, - "content_type": { - "type": "string" + "role_id": { + "type": "integer", + "nullable": true }, - "size_bytes": { + "user_id": { + "type": "integer", + "nullable": true + }, + "scope": { + "$ref": "#/components/schemas/RecipientScope" + }, + "id": { "type": "integer", - "minimum": 0, "nullable": true + }, + "feature": { + "type": "string" + }, + "event": { + "type": "string" } }, "required": [ - "content_type", - "evidence_type" + "event", + "feature", + "id", + "kind" ] }, - "PublicToolLoan": { + "RecipientRulesPut": { "type": "object", "properties": { - "has_kit_checklist": { - "type": "boolean", - "readOnly": true - }, - "public_token": { + "feature": { "type": "string", - "format": "uuid", - "readOnly": true + "maxLength": 32 }, - "status": { + "event": { "type": "string", - "readOnly": true + "maxLength": 64 }, - "items": { + "rules": { "type": "array", "items": { - "$ref": "#/components/schemas/PublicToolLoanItem" - }, - "readOnly": true + "$ref": "#/components/schemas/RecipientRule" + } } }, "required": [ - "has_kit_checklist", - "items", - "public_token", - "status" + "event", + "feature", + "rules" ] }, - "PublicToolLoanItem": { + "RecipientRulesResponse": { "type": "object", "properties": { - "product_name": { - "type": "string" + "delegated": { + "type": "boolean" }, - "quantity": { - "type": "integer" - } - }, - "required": [ - "product_name", - "quantity" - ] - }, - "PublicToolScan": { - "type": "object", - "properties": { - "kit_checklist": { + "features": { "type": "array", "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 + "$ref": "#/components/schemas/RecipientFeature" + } }, - "payload": { - "type": "string", - "maxLength": 64 + "roles": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecipientRole" + } }, - "evidence_id": { - "type": "integer" + "members": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecipientMember" + } }, - "remark": { - "type": "string" + "rules": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecipientRuleOutput" + } }, - "report_problem": { - "type": "boolean", - "default": false + "managed_policy_markers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ManagedPolicyMarker" + } }, - "problem_note": { - "type": "string", - "default": "" + "scope_options": { + "$ref": "#/components/schemas/RecipientScopeOptions" } }, "required": [ - "evidence_id", - "payload", - "remark" + "delegated", + "features", + "managed_policy_markers", + "members", + "roles", + "rules", + "scope_options" ] }, - "PublicTypeIntake": { + "RecipientScope": { "type": "object", "properties": { - "materials": { + "machine_type_ids": { "type": "array", "items": { - "$ref": "#/components/schemas/PublicMaterial" + "type": "integer" } }, - "intake_schema": {} - }, - "required": [ - "intake_schema", - "materials" - ] - }, - "PurposeEnum": { - "enum": [ - "enroll", - "rotate", - "reactivate" - ], - "type": "string", - "description": "* `enroll` - Enroll\n* `rotate` - Rotate\n* `reactivate` - Reactivate" + "machine_ids": { + "type": "array", + "items": { + "type": "integer" + } + }, + "category_ids": { + "type": "array", + "items": { + "type": "integer" + } + } + } }, - "PushDevice": { + "RecipientScopeOption": { "type": "object", "properties": { "id": { "type": "integer" }, - "provider": { - "type": "string" - }, - "environment": { + "name": { "type": "string" - }, - "makerspace_id": { - "type": "integer" - }, - "active": { - "type": "boolean" - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" } }, "required": [ - "active", - "created_at", - "environment", "id", - "makerspace_id", - "provider", - "updated_at" + "name" ] }, - "PushDeviceRegistration": { + "RecipientScopeOptions": { "type": "object", "properties": { - "token": { - "type": "string", - "writeOnly": true, - "maxLength": 4096, - "pattern": "^[A-Za-z0-9_:.-]{20,4096}$" + "machine_types": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecipientScopeOption" + } }, - "provider": { - "$ref": "#/components/schemas/PushDeviceRegistrationProviderEnum" + "machines": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecipientScopeOption" + } }, - "environment": { - "$ref": "#/components/schemas/EnvironmentEnum" + "categories": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecipientScopeOption" + } } }, "required": [ - "environment", - "provider", - "token" + "categories", + "machine_types", + "machines" ] }, - "PushDeviceRegistrationProviderEnum": { - "enum": [ - "fcm", - "apns" - ], - "type": "string", - "description": "* `fcm` - FCM\n* `apns` - APNs" + "RecoveryAcknowledge": { + "type": "object", + "properties": { + "acknowledgement": { + "type": "string" + } + }, + "required": [ + "acknowledgement" + ] }, - "QrCode": { + "RecoveryState": { "type": "object", "properties": { - "id": { - "type": "integer", + "mode": { + "allOf": [ + { + "$ref": "#/components/schemas/RecoveryStateModeEnum" + } + ], "readOnly": true }, - "makerspace": { - "type": "integer" - }, - "payload": { + "auth_generation": { "type": "string", + "format": "uuid", "readOnly": true }, - "target_type": { - "$ref": "#/components/schemas/QrCodeTargetTypeEnum" + "active_restore": { + "type": "string", + "format": "uuid", + "readOnly": true, + "nullable": true }, - "target_id": { + "recovery_principal": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/Status37fEnum" - } - ], "readOnly": true, - "default": "active" + "nullable": true }, - "created_at": { + "quarantine_reason": { "type": "string", - "format": "date-time", "readOnly": true }, - "updated_at": { + "quarantined_at": { "type": "string", "format": "date-time", - "readOnly": true + "readOnly": true, + "nullable": true }, - "revoked_at": { + "acknowledged_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true + }, + "acknowledged_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "acknowledgement": { + "type": "string", + "readOnly": true + }, + "residual_risk": { + "type": "string", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ - "created_at", - "id", - "makerspace", - "payload", - "revoked_at", - "status", - "target_id", - "target_type", + "acknowledged_at", + "acknowledged_by", + "acknowledgement", + "active_restore", + "auth_generation", + "mode", + "quarantine_reason", + "quarantined_at", + "recovery_principal", + "residual_risk", "updated_at" ] }, - "QrCodeTargetTypeEnum": { + "RecoveryStateModeEnum": { "enum": [ - "box", - "product", - "asset", - "member_card" + "normal", + "target_import", + "quiesced", + "quarantined" ], "type": "string", - "description": "* `box` - Box\n* `product` - Product\n* `asset` - Asset\n* `member_card` - Member card" + "description": "* `normal` - Normal\n* `target_import` - Target import\n* `quiesced` - Quiesced\n* `quarantined` - Quarantined" }, - "QrHistoryItem": { + "RecoveryUnavailable": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "source": { + "detail": { "type": "string" }, - "context": { + "code": { "type": "string" - }, - "actor": { - "type": "integer", - "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time" - } - }, - "required": [ - "actor", - "context", - "created_at", - "id", - "source" - ] - }, - "QrPrintBatch": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer" - }, - "title": { - "type": "string", - "maxLength": 200 - }, - "status": { - "$ref": "#/components/schemas/QrPrintBatchStatusEnum" - }, - "created_by": { - "type": "integer", - "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "printed_at": { - "type": "string", - "format": "date-time", - "nullable": true } }, "required": [ - "created_at", - "id", - "makerspace", - "title" + "code", + "detail" ] }, - "QrPrintBatchCreate": { + "RecurrenceResult": { "type": "object", "properties": { - "title": { - "type": "string" + "occurrences": { + "type": "array", + "items": { + "$ref": "#/components/schemas/OccurrenceResult" + } } }, "required": [ - "title" + "occurrences" ] }, - "QrPrintBatchDetail": { + "RecurringBookingInput": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer" - }, - "title": { + "certification_override_reason": { "type": "string", - "maxLength": 200 - }, - "status": { - "$ref": "#/components/schemas/QrPrintBatchStatusEnum" - }, - "created_by": { - "type": "integer", - "nullable": true + "description": "Requires existing authority over the linked machine type; audited.", + "maxLength": 1000 }, - "created_at": { + "starts_at": { "type": "string", "format": "date-time", - "readOnly": true + "writeOnly": true }, - "printed_at": { + "ends_at": { "type": "string", "format": "date-time", + "writeOnly": true + }, + "custom_answers": { + "writeOnly": true, "nullable": true }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QrPrintBatchItem" - }, - "readOnly": true - } - }, - "required": [ - "created_at", - "id", - "items", - "makerspace", - "title" - ] - }, - "QrPrintBatchItem": { - "type": "object", - "properties": { - "id": { + "member_id": { "type": "integer", - "readOnly": true - }, - "qr_code": { - "allOf": [ - { - "$ref": "#/components/schemas/QrCode" - } - ], - "readOnly": true - }, - "label_text": { - "type": "string", - "maxLength": 255 + "maximum": 9223372036854775807, + "minimum": 1, + "format": "int64" }, - "target_type": { + "note": { "type": "string", - "maxLength": 20 + "maxLength": 5000 }, - "target_id": { + "count": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "maximum": 52, + "minimum": 2 }, - "sort_order": { + "interval_weeks": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "maximum": 12, + "minimum": 1, + "default": 1 } }, "required": [ - "id", - "label_text", - "qr_code", - "target_id", - "target_type" + "count", + "ends_at", + "member_id", + "starts_at" ] }, - "QrPrintBatchItemCreate": { + "ReferralCreate": { "type": "object", "properties": { - "qr_code_id": { - "type": "integer" - }, - "label_text": { - "type": "string" - }, - "sort_order": { - "type": "integer", - "minimum": 0 + "invite_email": { + "type": "string", + "format": "email", + "maxLength": 254 } }, "required": [ - "qr_code_id" + "invite_email" ] }, - "QrPrintBatchItemResult": { + "ReferralOutcome": { "type": "object", "properties": { - "id": { - "type": "integer" + "state": { + "type": "string" } }, "required": [ - "id" + "state" ] }, - "QrPrintBatchStatusEnum": { - "enum": [ - "draft", - "printed", - "archived" - ], - "type": "string", - "description": "* `draft` - Draft\n* `printed` - Printed\n* `archived` - Archived" - }, - "QrRebindResult": { + "RefreshResponse": { "type": "object", "properties": { - "qr": { - "$ref": "#/components/schemas/QrCode" - }, - "target": { - "type": "object", - "additionalProperties": {} + "access": { + "type": "string" } }, "required": [ - "qr", - "target" + "access" ] }, - "QrRebindTarget": { + "Refund": { "type": "object", "properties": { - "target_type": { - "$ref": "#/components/schemas/QrRebindTargetTargetTypeEnum" + "id": { + "type": "integer", + "readOnly": true }, - "target_id": { - "type": "integer" + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true }, - "destination_makerspace_id": { - "type": "integer", + "currency": { + "type": "string", + "readOnly": true + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/RefundStatusEnum" + } + ], + "readOnly": true + }, + "provider": { + "allOf": [ + { + "$ref": "#/components/schemas/RefundProviderEnum" + } + ], + "readOnly": true + }, + "reason": { + "type": "string", + "readOnly": true + }, + "method": { + "allOf": [ + { + "$ref": "#/components/schemas/Method2edEnum" + } + ], + "readOnly": true + }, + "reference": { + "type": "string", + "readOnly": true + }, + "refunded_at": { + "type": "string", + "format": "date-time", + "readOnly": true, "nullable": true }, - "destination_product_id": { + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "settled_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "amends": { "type": "integer", + "readOnly": true, "nullable": true }, - "new_name": { - "type": "string", - "maxLength": 100 + "corrected": { + "type": "boolean", + "readOnly": true } }, "required": [ - "target_id", - "target_type" + "amends", + "amount", + "corrected", + "created_at", + "currency", + "id", + "method", + "provider", + "reason", + "reference", + "refunded_at", + "settled_at", + "status" ] }, - "QrRebindTargetTargetTypeEnum": { + "RefundProviderEnum": { "enum": [ - "product", - "asset" + "stripe", + "razorpay", + "manual" ], "type": "string", - "description": "* `product` - product\n* `asset` - asset" + "description": "* `stripe` - Stripe\n* `razorpay` - Razorpay\n* `manual` - Manual" }, - "QrResolve": { - "type": "object", - "properties": { - "payload": { - "type": "string" - } - }, - "required": [ - "payload" - ] + "RefundStatusEnum": { + "enum": [ + "pending", + "succeeded", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `succeeded` - Succeeded\n* `failed` - Failed" }, - "QrResolveResult": { + "RegistrationReadiness": { "type": "object", "properties": { - "qr": { - "$ref": "#/components/schemas/QrCode" + "hint": { + "type": "string" }, - "target": { + "production": { + "type": "boolean" + }, + "login_ready": { + "type": "boolean" + }, + "login": { "type": "object", - "additionalProperties": {} + "additionalProperties": { + "$ref": "#/components/schemas/ReadinessItem" + } }, - "allowed_actions": { - "type": "array", - "items": { - "type": "string" + "optional": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/ReadinessItem" } } }, "required": [ - "allowed_actions", - "qr", - "target" + "hint", + "login", + "login_ready", + "optional", + "production" ] }, - "QrScan": { + "Rehydrate": { "type": "object", "properties": { - "payload": { - "type": "string" - }, - "context": { - "$ref": "#/components/schemas/ContextEnum" - }, - "request_id": { - "type": "integer", - "nullable": true + "destination": { + "type": "integer" } }, "required": [ - "context", - "payload" + "destination" ] }, - "QrScanResult": { + "RejectRequest": { "type": "object", "properties": { - "qr": { - "$ref": "#/components/schemas/QrCode" - }, - "target": { - "type": "object", - "additionalProperties": {} - }, - "scan_id": { - "type": "integer" + "reason": { + "type": "string" } }, "required": [ - "qr", - "scan_id", - "target" + "reason" ] }, - "QrScansReport": { + "RejoinDuesRuleEnum": { + "enum": [ + "once", + "each_join" + ], + "type": "string", + "description": "* `once` - Joining dues once\n* `each_join` - Joining dues on each join" + }, + "RelationshipEnum": { + "enum": [ + "owner", + "manager", + "affiliate" + ], + "type": "string", + "description": "* `owner` - Owner\n* `manager` - Manager\n* `affiliate` - Affiliate" + }, + "ReorderDraft": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QrScansReportRow" - } + "product_id": { + "type": "integer", + "minimum": 1 } }, "required": [ - "rows", - "typed_rows" + "product_id" ] }, - "QrScansReportRow": { + "ReorderSuggestion": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" + "id": { + "type": "integer", + "readOnly": true }, - "context": { - "type": "string" + "name": { + "type": "string", + "readOnly": true }, - "count": { - "type": "integer" - } - }, - "required": [ - "context", - "count" - ] - }, - "QuantityChainSummary": { - "type": "object", - "properties": { - "loan_count": { - "type": "integer" + "manufacturer": { + "type": "string", + "readOnly": true }, - "direct_loan_count": { - "type": "integer" + "model": { + "type": "string", + "readOnly": true }, - "issued_quantity": { - "type": "integer" + "sku": { + "type": "string", + "readOnly": true }, - "returned_quantity": { - "type": "integer" + "barcode": { + "type": "string", + "readOnly": true }, - "damaged_quantity": { - "type": "integer" + "supplier": { + "type": "string", + "readOnly": true }, - "missing_quantity": { - "type": "integer" + "available_quantity": { + "type": "integer", + "readOnly": true }, - "active_quantity": { - "type": "integer" + "reorder_point": { + "type": "integer", + "readOnly": true + }, + "reorder_quantity": { + "type": "integer", + "readOnly": true } }, "required": [ - "active_quantity", - "damaged_quantity", - "direct_loan_count", - "issued_quantity", - "loan_count", - "missing_quantity", - "returned_quantity" + "available_quantity", + "barcode", + "id", + "manufacturer", + "model", + "name", + "reorder_point", + "reorder_quantity", + "sku", + "supplier" ] }, - "RateLimitTierEnum": { - "enum": [ - "public", - "standard", - "trusted" - ], - "type": "string", - "description": "* `public` - Public\n* `standard` - Standard\n* `trusted` - Trusted" - }, - "ReadAll": { + "ReplayDirectHandout": { "type": "object", "properties": { - "updated": { + "kit_checklist": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitCount" + }, + "maxItems": 3550 + }, + "borrower_id": { "type": "integer" - } - }, - "required": [ - "updated" - ] - }, - "Readiness": { - "type": "object", - "properties": { - "status": { - "type": "string" }, - "database": { + "remark": { "type": "string" }, - "archive_custody": { - "$ref": "#/components/schemas/ArchiveCustodyReadiness" - } - }, - "required": [ - "archive_custody", - "database", - "status" - ] - }, - "Reason870Enum": { - "enum": [ - "bounce", - "complaint" - ], - "type": "string", - "description": "* `bounce` - Hard bounce\n* `complaint` - Spam complaint" - }, - "ReceiptEnvelope": { - "type": "object", - "properties": { - "payload": { - "type": "object", - "additionalProperties": {} + "container_id": { + "type": "integer", + "nullable": true }, - "signer_fingerprint": { - "type": "string", - "maxLength": 64 + "qr_payloads": { + "type": "array", + "items": { + "type": "string", + "maxLength": 64 + }, + "maxItems": 50 }, - "signature": { - "type": "string", - "maxLength": 88 + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DirectLoanItem" + } } }, "required": [ - "payload", - "signature", - "signer_fingerprint" + "borrower_id" ] }, - "RecentlyAddedReport": { + "ReplayDirectReturn": { "type": "object", "properties": { - "rows": { + "loan_id": { + "type": "integer", + "minimum": 1 + }, + "kit_checklist": { "type": "array", "items": { - "type": "array", - "items": {} - } + "$ref": "#/components/schemas/KitCount" + }, + "maxItems": 3550 }, - "typed_rows": { + "notes": { + "type": "string" + }, + "qr_payload": { + "type": "string", + "maxLength": 64 + }, + "resolutions": { "type": "array", "items": { - "$ref": "#/components/schemas/RecentlyAddedReportRow" + "$ref": "#/components/schemas/ReturnItemResolution" } } }, "required": [ - "rows", - "typed_rows" + "notes" ] }, - "RecentlyAddedReportRow": { + "ReplayDomainPayload": { + "oneOf": [ + { + "$ref": "#/components/schemas/KioskAssignBox" + }, + { + "$ref": "#/components/schemas/ReplayReviewedIssue" + }, + { + "$ref": "#/components/schemas/ReplayReviewedReturn" + }, + { + "$ref": "#/components/schemas/ReplayDirectHandout" + }, + { + "$ref": "#/components/schemas/ReplayDirectReturn" + } + ] + }, + "ReplayEnvelope": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" + "client_command_id": { + "type": "string", + "format": "uuid" }, - "product_name": { - "type": "string" + "kind": { + "$ref": "#/components/schemas/ReplayEnvelopeKindEnum" }, - "created_at": { + "version": { + "type": "integer", + "minimum": 1 + }, + "aggregate_id": { + "type": "string", + "format": "uuid" + }, + "sequence": { + "type": "integer", + "maximum": 10000, + "minimum": 1 + }, + "predecessor_client_id": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "client_created_at": { "type": "string", "format": "date-time" }, - "total_quantity": { - "type": "integer" + "payload": { + "$ref": "#/components/schemas/ReplayDomainPayload" + }, + "evidence": { + "allOf": [ + { + "$ref": "#/components/schemas/KioskEvidencePrepare" + } + ], + "nullable": true } }, "required": [ - "created_at", - "product_name", - "total_quantity" + "aggregate_id", + "client_command_id", + "client_created_at", + "kind", + "payload", + "sequence", + "version" ] }, - "RecipeEnum": { - "enum": [ - "free_open", - "free_vouch", - "paid_approval", - "paid_invite_only", - "closed" - ], - "type": "string", - "description": "* `free_open` - free_open\n* `free_vouch` - free_vouch\n* `paid_approval` - paid_approval\n* `paid_invite_only` - paid_invite_only\n* `closed` - closed" - }, - "RecipientActionsEnum": { + "ReplayEnvelopeKindEnum": { "enum": [ - "accept_request", "assign_box", - "collect_service_request", - "edit_inventory", - "issue_direct_loan", - "issue_request", - "manage_bookings", - "manage_devices", - "manage_events", - "manage_machines", - "manage_makerspace", - "manage_member_cards", - "manage_members", - "manage_printing", - "manage_qr", - "record_payment", - "reject_request", - "return_request", - "scan_member_cards", - "upload_evidence", - "view_audit", - "view_finance", - "view_inventory" + "reviewed_issue", + "reviewed_return", + "direct_handout", + "direct_return" ], "type": "string", - "description": "* `accept_request` - accept_request\n* `assign_box` - assign_box\n* `collect_service_request` - collect_service_request\n* `edit_inventory` - edit_inventory\n* `issue_direct_loan` - issue_direct_loan\n* `issue_request` - issue_request\n* `manage_bookings` - manage_bookings\n* `manage_devices` - manage_devices\n* `manage_events` - manage_events\n* `manage_machines` - manage_machines\n* `manage_makerspace` - manage_makerspace\n* `manage_member_cards` - manage_member_cards\n* `manage_members` - manage_members\n* `manage_printing` - manage_printing\n* `manage_qr` - manage_qr\n* `record_payment` - record_payment\n* `reject_request` - reject_request\n* `return_request` - return_request\n* `scan_member_cards` - scan_member_cards\n* `upload_evidence` - upload_evidence\n* `view_audit` - view_audit\n* `view_finance` - view_finance\n* `view_inventory` - view_inventory" - }, - "RecipientFeature": { - "type": "object", - "properties": { - "key": { - "type": "string" - }, - "events": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": [ - "events", - "key" - ] + "description": "* `assign_box` - assign_box\n* `reviewed_issue` - reviewed_issue\n* `reviewed_return` - reviewed_return\n* `direct_handout` - direct_handout\n* `direct_return` - direct_return" }, - "RecipientMember": { + "ReplayRecovery": { "type": "object", "properties": { "id": { - "type": "integer" + "type": "string", + "format": "uuid", + "readOnly": true }, - "username": { + "makerspace_id": { + "type": "integer", + "readOnly": true + }, + "station_id": { + "type": "integer", + "readOnly": true + }, + "station_name": { "type": "string" }, - "email": { + "station_kind": { + "type": "string" + }, + "operator_display": { + "type": "string", + "readOnly": true + }, + "operation_kind": { + "type": "string", + "maxLength": 24 + }, + "operation_version": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "aggregate_id": { + "type": "string", + "format": "uuid" + }, + "sequence": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "state": { + "$ref": "#/components/schemas/ReplayRecoveryStateEnum" + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "client_created_at": { + "type": "string", + "format": "date-time" + }, + "completed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "expires_at": { + "type": "string", + "format": "date-time" + }, + "retained_until": { + "type": "string", + "readOnly": true + }, + "failure_code": { + "type": "string", + "nullable": true, + "maxLength": 64 + }, + "failure_detail": { + "type": "string", + "nullable": true, + "maxLength": 200 + }, + "subject_request_id": { + "type": "integer", + "nullable": true + }, + "subject_loan_id": { + "type": "integer", + "nullable": true + }, + "recovery_request_id": { + "type": "integer", + "nullable": true, + "readOnly": true + }, + "recovery_loan_id": { + "type": "integer", + "nullable": true, + "readOnly": true + }, + "result_request_id": { + "type": "integer", + "nullable": true + }, + "result_loan_id": { + "type": "integer", + "nullable": true + }, + "result_return_event_id": { + "type": "integer", + "nullable": true + }, + "predecessor_id": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "predecessor_state": { + "type": "string", + "nullable": true + }, + "evidence_id": { + "type": "integer", + "nullable": true + }, + "can_view_evidence": { + "type": "boolean", + "readOnly": true + }, + "resolution_code": { + "nullable": true, "oneOf": [ { - "type": "string", - "format": "email" + "$ref": "#/components/schemas/ResolutionCodeEnum" }, { - "type": "string", - "maxLength": 0 + "$ref": "#/components/schemas/BlankEnum" + }, + { + "$ref": "#/components/schemas/NullEnum" } ] + }, + "resolved_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "resolved_by_display": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "replacement_command_id": { + "type": "string", + "format": "uuid", + "nullable": true } }, "required": [ - "email", + "aggregate_id", + "can_view_evidence", + "client_created_at", + "created_at", + "evidence_id", + "expires_at", "id", - "username" + "makerspace_id", + "operation_kind", + "operation_version", + "operator_display", + "predecessor_id", + "recovery_loan_id", + "recovery_request_id", + "replacement_command_id", + "resolved_by_display", + "result_loan_id", + "result_request_id", + "result_return_event_id", + "retained_until", + "sequence", + "station_id", + "station_kind", + "station_name", + "subject_loan_id", + "subject_request_id" ] }, - "RecipientRole": { + "ReplayRecoveryStateEnum": { + "enum": [ + "prepared", + "claimed", + "succeeded", + "failed" + ], + "type": "string", + "description": "* `prepared` - Prepared\n* `claimed` - Claimed\n* `succeeded` - Succeeded\n* `failed` - Failed" + }, + "ReplayResolution": { "type": "object", "properties": { - "id": { - "type": "integer" - }, - "name": { - "type": "string" + "resolution_code": { + "$ref": "#/components/schemas/ResolutionCodeEnum" }, - "slug": { - "type": "string" + "replacement_command_id": { + "type": "string", + "format": "uuid", + "nullable": true } }, "required": [ - "id", - "name", - "slug" + "resolution_code" ] }, - "RecipientRule": { + "ReplayResult": { "type": "object", "properties": { + "replay_id": { + "type": "string", + "format": "uuid" + }, + "client_command_id": { + "type": "string", + "format": "uuid" + }, + "state": { + "$ref": "#/components/schemas/ReplayResultStateEnum" + }, "kind": { - "$ref": "#/components/schemas/KindE56Enum" + "type": "string" }, - "role_id": { + "expires_at": { + "type": "string", + "format": "date-time" + }, + "evidence_id": { "type": "integer", "nullable": true }, - "user_id": { + "result_request_id": { "type": "integer", "nullable": true }, - "scope": { - "$ref": "#/components/schemas/RecipientScope" + "result_loan_id": { + "type": "integer", + "nullable": true + }, + "result_return_event_id": { + "type": "integer", + "nullable": true + }, + "failure_code": { + "type": "string", + "nullable": true + }, + "failure_detail": { + "type": "string", + "nullable": true + }, + "upload": { + "allOf": [ + { + "$ref": "#/components/schemas/EvidenceUrlResponse" + } + ], + "nullable": true } }, "required": [ - "kind" + "client_command_id", + "evidence_id", + "expires_at", + "failure_code", + "failure_detail", + "kind", + "replay_id", + "result_loan_id", + "result_request_id", + "result_return_event_id", + "state" ] }, - "RecipientRuleOutput": { + "ReplayResultStateEnum": { + "enum": [ + "prepared", + "claimed", + "succeeded", + "failed" + ], + "type": "string", + "description": "* `prepared` - prepared\n* `claimed` - claimed\n* `succeeded` - succeeded\n* `failed` - failed" + }, + "ReplayReviewedIssue": { "type": "object", "properties": { - "kind": { - "$ref": "#/components/schemas/KindE56Enum" - }, - "role_id": { + "request_id": { "type": "integer", - "nullable": true + "minimum": 1 }, - "user_id": { - "type": "integer", - "nullable": true + "kit_checklist": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitCount" + }, + "maxItems": 3550 }, - "scope": { - "$ref": "#/components/schemas/RecipientScope" + "remark": { + "type": "string", + "default": "" }, - "id": { + "asset_qr_payloads": { + "type": "array", + "items": { + "type": "string" + } + }, + "rejects": { + "type": "array", + "items": { + "$ref": "#/components/schemas/IssueReject" + } + } + }, + "required": [ + "request_id" + ] + }, + "ReplayReviewedReturn": { + "type": "object", + "properties": { + "request_id": { "type": "integer", - "nullable": true + "minimum": 1 }, - "feature": { + "kit_checklist": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KitCount" + }, + "maxItems": 3550 + }, + "box_code": { "type": "string" }, - "event": { + "remark": { "type": "string" + }, + "resolutions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReturnItemResolution" + } } }, "required": [ - "event", - "feature", - "id", - "kind" + "box_code", + "remark", + "request_id", + "resolutions" ] }, - "RecipientRulesPut": { + "ReportCatalog": { "type": "object", "properties": { - "feature": { - "type": "string", - "maxLength": 32 - }, - "event": { - "type": "string", - "maxLength": 64 - }, - "rules": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/RecipientRule" + "$ref": "#/components/schemas/ReportCatalogItem" } } }, "required": [ - "event", - "feature", - "rules" + "results" ] }, - "RecipientRulesResponse": { + "ReportCatalogItem": { "type": "object", "properties": { - "delegated": { - "type": "boolean" + "key": { + "type": "string" }, - "features": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RecipientFeature" - } + "title": { + "type": "string" }, - "roles": { + "fields": { "type": "array", "items": { - "$ref": "#/components/schemas/RecipientRole" + "type": "string" } }, - "members": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RecipientMember" - } + "exportable": { + "type": "boolean" }, - "rules": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RecipientRuleOutput" - } + "summary": { + "type": "boolean" }, - "managed_policy_markers": { + "required_modules": { "type": "array", "items": { - "$ref": "#/components/schemas/ManagedPolicyMarker" + "type": "string" } }, - "scope_options": { - "$ref": "#/components/schemas/RecipientScopeOptions" - } - }, - "required": [ - "delegated", - "features", - "managed_policy_markers", - "members", - "roles", - "rules", - "scope_options" - ] - }, - "RecipientScope": { - "type": "object", - "properties": { - "machine_type_ids": { + "available": { + "type": "boolean", + "nullable": true + }, + "unavailable_reason": { + "type": "string", + "nullable": true + }, + "grains": { "type": "array", "items": { - "type": "integer" + "type": "string" } }, - "machine_ids": { + "chart_hint": { + "type": "string" + }, + "chart_spec": { + "allOf": [ + { + "$ref": "#/components/schemas/ReportChartSpec" + } + ], + "nullable": true + }, + "identity_bearing": { + "type": "boolean" + }, + "aggregate_supported": { + "type": "boolean" + }, + "allowed_formats": { "type": "array", "items": { - "type": "integer" + "$ref": "#/components/schemas/AllowedFormatsEnum" } }, - "category_ids": { + "schedulable": { + "type": "boolean" + }, + "previewable": { + "type": "boolean" + }, + "condition_metrics": { "type": "array", "items": { - "type": "integer" + "type": "string" } } - } + }, + "required": [ + "aggregate_supported", + "allowed_formats", + "available", + "chart_hint", + "chart_spec", + "condition_metrics", + "exportable", + "fields", + "grains", + "identity_bearing", + "key", + "previewable", + "required_modules", + "schedulable", + "summary", + "title", + "unavailable_reason" + ] }, - "RecipientScopeOption": { + "ReportChartAxis": { "type": "object", "properties": { - "id": { - "type": "integer" - }, - "name": { + "field": { "type": "string" + }, + "kind": { + "$ref": "#/components/schemas/ReportChartAxisKindEnum" } }, "required": [ - "id", - "name" + "field", + "kind" ] }, - "RecipientScopeOptions": { + "ReportChartAxisKindEnum": { + "enum": [ + "time", + "category" + ], + "type": "string", + "description": "* `time` - time\n* `category` - category" + }, + "ReportChartSpec": { "type": "object", "properties": { - "machine_types": { + "series": { "type": "array", "items": { - "$ref": "#/components/schemas/RecipientScopeOption" + "type": "string" } }, - "machines": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RecipientScopeOption" - } + "x": { + "$ref": "#/components/schemas/ReportChartAxis" }, - "categories": { + "values": { "type": "array", "items": { - "$ref": "#/components/schemas/RecipientScopeOption" - } + "$ref": "#/components/schemas/ReportChartValue" + }, + "description": "Empty for detail tables without numeric measures." + }, + "currency_field": { + "type": "string", + "nullable": true + }, + "partition": { + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/PartitionEnum" + }, + { + "$ref": "#/components/schemas/NullEnum" + } + ] + }, + "negatives": { + "$ref": "#/components/schemas/NegativesEnum" + }, + "truncation": { + "allOf": [ + { + "$ref": "#/components/schemas/ReportChartTruncation" + } + ], + "nullable": true + }, + "alt_template": { + "type": "string" } }, "required": [ - "categories", - "machine_types", - "machines" + "alt_template", + "currency_field", + "negatives", + "partition", + "series", + "truncation", + "values", + "x" ] }, - "RecoveryAcknowledge": { + "ReportChartTruncation": { "type": "object", "properties": { - "acknowledgement": { + "top_n": { + "type": "integer", + "minimum": 1 + }, + "other_label": { "type": "string" } }, "required": [ - "acknowledgement" + "other_label", + "top_n" ] }, - "RecoveryState": { + "ReportChartValue": { "type": "object", "properties": { - "mode": { + "field": { + "type": "string" + }, + "unit": { + "type": "string" + }, + "kind": { "allOf": [ { - "$ref": "#/components/schemas/RecoveryStateModeEnum" + "$ref": "#/components/schemas/ReportChartValueKindEnum" } ], - "readOnly": true + "description": "Optional series rendering: exclusive status counts stack; cumulative rates use lines.\n\n* `line` - line\n* `stacked_bar` - stacked_bar" + } + }, + "required": [ + "field", + "unit" + ] + }, + "ReportChartValueKindEnum": { + "enum": [ + "line", + "stacked_bar" + ], + "type": "string", + "description": "* `line` - line\n* `stacked_bar` - stacked_bar" + }, + "ReportCondition": { + "type": "object", + "properties": { + "non_empty": { + "type": "boolean" }, - "auth_generation": { + "metric": { "type": "string", - "format": "uuid", - "readOnly": true + "description": "Sum this numeric column across the matching report rows.", + "maxLength": 80 }, - "active_restore": { + "operator": { + "$ref": "#/components/schemas/OperatorEnum" + }, + "value": { "type": "string", - "format": "uuid", - "readOnly": true, - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,14}(?:\\.\\d{0,6})?$" }, - "recovery_principal": { + "currency": { + "type": "string", + "pattern": "^[a-z]{3}$" + } + } + }, + "ReportDelivery": { + "type": "object", + "properties": { + "id": { "type": "integer", - "readOnly": true, - "nullable": true + "readOnly": true }, - "quarantine_reason": { + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/ReportDeliveryStatusEnum" + } + ], + "readOnly": true + }, + "error": { "type": "string", "readOnly": true }, - "quarantined_at": { + "created_at": { "type": "string", "format": "date-time", - "readOnly": true, - "nullable": true + "readOnly": true }, - "acknowledged_at": { + "expires_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "acknowledged_by": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "acknowledgement": { - "type": "string", - "readOnly": true - }, - "residual_risk": { - "type": "string", - "readOnly": true - }, - "updated_at": { + "download_url": { "type": "string", - "format": "date-time", + "format": "uri", "readOnly": true } }, "required": [ - "acknowledged_at", - "acknowledged_by", - "acknowledgement", - "active_restore", - "auth_generation", - "mode", - "quarantine_reason", - "quarantined_at", - "recovery_principal", - "residual_risk", - "updated_at" + "created_at", + "download_url", + "error", + "expires_at", + "id", + "status" ] }, - "RecoveryStateModeEnum": { + "ReportDeliveryStatusEnum": { "enum": [ - "normal", - "target_import", - "quiesced", - "quarantined" + "sent", + "failed" ], "type": "string", - "description": "* `normal` - Normal\n* `target_import` - Target import\n* `quiesced` - Quiesced\n* `quarantined` - Quarantined" + "description": "* `sent` - Sent\n* `failed` - Failed" }, - "RecoveryUnavailable": { + "ReportError": { "type": "object", "properties": { "detail": { @@ -99146,162 +122202,154 @@ } }, "required": [ - "code", "detail" ] }, - "RecurrenceResult": { - "type": "object", - "properties": { - "occurrences": { - "type": "array", - "items": { - "$ref": "#/components/schemas/OccurrenceResult" - } - } - }, - "required": [ - "occurrences" - ] + "ReportKeyEnum": { + "enum": [ + "payment-cash-book", + "taken-items", + "active-loans", + "returns", + "damaged-missing", + "top-borrowers", + "recently-added", + "event-attendance", + "booking-utilization", + "member-activity", + "printer-service", + "fablab-health", + "payment-reconciliation", + "evidence-compliance", + "import-quality", + "communications-health", + "community-engagement", + "certification-coverage", + "audit-trail", + "membership-growth", + "member-retention", + "membership-revenue", + "payments-ledger", + "revenue-over-time", + "cash-book", + "payments-ageing", + "accounting-journal", + "event-registrations", + "event-feedback", + "event-attendance-pdf", + "booking-resource-utilization", + "booking-resource-utilization-pdf", + "booking-reservations", + "booking-answers", + "mobile-devices", + "mobile-auth", + "booking-occupancy", + "booking-lead-time", + "booking-top-members", + "event-signups", + "event-revenue", + "event-funnel", + "event-checkin-times", + "event-feedback-scores", + "most-lent", + "asset-usage", + "loan-throughput", + "inventory-utilisation", + "overdue-loans", + "damaged-lost", + "longest-loans", + "stock-health", + "request-funnel", + "problem-reports", + "inventory-watch-demand", + "summary", + "qr-scans", + "qr-scan-actors", + "inventory-control", + "consumable-usage", + "consumable-pools", + "procurement-performance", + "stock-transfers", + "stocktake-variance", + "machine-usage", + "machine-usage-members", + "machine-usage-heatmap", + "machine-downtime", + "maintenance-activity", + "machine-service", + "service-queue-times", + "printer-jobs", + "machine-errors", + "warranty-expiry", + "device-health", + "presence-activity", + "presence-top-visitors", + "presence-activity-hours", + "audit-activity", + "audit-top-actors", + "audit-activity-hours", + "organization-membership", + "module-operational-health", + "platform-services", + "platform-usage" + ], + "type": "string", + "description": "* `payment-cash-book` - payment-cash-book\n* `taken-items` - taken-items\n* `active-loans` - active-loans\n* `returns` - returns\n* `damaged-missing` - damaged-missing\n* `top-borrowers` - top-borrowers\n* `recently-added` - recently-added\n* `event-attendance` - event-attendance\n* `booking-utilization` - booking-utilization\n* `member-activity` - member-activity\n* `printer-service` - printer-service\n* `fablab-health` - fablab-health\n* `payment-reconciliation` - payment-reconciliation\n* `evidence-compliance` - evidence-compliance\n* `import-quality` - import-quality\n* `communications-health` - communications-health\n* `community-engagement` - community-engagement\n* `certification-coverage` - certification-coverage\n* `audit-trail` - audit-trail\n* `membership-growth` - membership-growth\n* `member-retention` - member-retention\n* `membership-revenue` - membership-revenue\n* `payments-ledger` - payments-ledger\n* `revenue-over-time` - revenue-over-time\n* `cash-book` - cash-book\n* `payments-ageing` - payments-ageing\n* `accounting-journal` - accounting-journal\n* `event-registrations` - event-registrations\n* `event-feedback` - event-feedback\n* `event-attendance-pdf` - event-attendance-pdf\n* `booking-resource-utilization` - booking-resource-utilization\n* `booking-resource-utilization-pdf` - booking-resource-utilization-pdf\n* `booking-reservations` - booking-reservations\n* `booking-answers` - booking-answers\n* `mobile-devices` - mobile-devices\n* `mobile-auth` - mobile-auth\n* `booking-occupancy` - booking-occupancy\n* `booking-lead-time` - booking-lead-time\n* `booking-top-members` - booking-top-members\n* `event-signups` - event-signups\n* `event-revenue` - event-revenue\n* `event-funnel` - event-funnel\n* `event-checkin-times` - event-checkin-times\n* `event-feedback-scores` - event-feedback-scores\n* `most-lent` - most-lent\n* `asset-usage` - asset-usage\n* `loan-throughput` - loan-throughput\n* `inventory-utilisation` - inventory-utilisation\n* `overdue-loans` - overdue-loans\n* `damaged-lost` - damaged-lost\n* `longest-loans` - longest-loans\n* `stock-health` - stock-health\n* `request-funnel` - request-funnel\n* `problem-reports` - problem-reports\n* `inventory-watch-demand` - inventory-watch-demand\n* `summary` - summary\n* `qr-scans` - qr-scans\n* `qr-scan-actors` - qr-scan-actors\n* `inventory-control` - inventory-control\n* `consumable-usage` - consumable-usage\n* `consumable-pools` - consumable-pools\n* `procurement-performance` - procurement-performance\n* `stock-transfers` - stock-transfers\n* `stocktake-variance` - stocktake-variance\n* `machine-usage` - machine-usage\n* `machine-usage-members` - machine-usage-members\n* `machine-usage-heatmap` - machine-usage-heatmap\n* `machine-downtime` - machine-downtime\n* `maintenance-activity` - maintenance-activity\n* `machine-service` - machine-service\n* `service-queue-times` - service-queue-times\n* `printer-jobs` - printer-jobs\n* `machine-errors` - machine-errors\n* `warranty-expiry` - warranty-expiry\n* `device-health` - device-health\n* `presence-activity` - presence-activity\n* `presence-top-visitors` - presence-top-visitors\n* `presence-activity-hours` - presence-activity-hours\n* `audit-activity` - audit-activity\n* `audit-top-actors` - audit-top-actors\n* `audit-activity-hours` - audit-activity-hours\n* `organization-membership` - organization-membership\n* `module-operational-health` - module-operational-health\n* `platform-services` - platform-services\n* `platform-usage` - platform-usage" }, - "RecurringBookingInput": { + "ReportSchedule": { "type": "object", "properties": { - "certification_override_reason": { - "type": "string", - "description": "Requires existing authority over the linked machine type; audited.", - "maxLength": 1000 - }, - "starts_at": { - "type": "string", - "format": "date-time", - "writeOnly": true + "id": { + "type": "integer", + "readOnly": true }, - "ends_at": { - "type": "string", - "format": "date-time", - "writeOnly": true + "makerspace": { + "type": "integer", + "readOnly": true }, - "custom_answers": { - "writeOnly": true, - "nullable": true + "report_key": { + "$ref": "#/components/schemas/ReportKeyEnum" }, - "member_id": { - "type": "integer", - "maximum": 9223372036854775807, - "minimum": 1, - "format": "int64" + "filters": { + "$ref": "#/components/schemas/ReportScheduleFilters" }, - "note": { + "grain": { "type": "string", - "maxLength": 5000 + "maxLength": 8 }, - "count": { - "type": "integer", - "maximum": 52, - "minimum": 2 + "format": { + "$ref": "#/components/schemas/ReportScheduleFormatEnum" }, - "interval_weeks": { - "type": "integer", - "maximum": 12, - "minimum": 1, - "default": 1 - } - }, - "required": [ - "count", - "ends_at", - "member_id", - "starts_at" - ] - }, - "ReferralCreate": { - "type": "object", - "properties": { - "invite_email": { - "type": "string", - "format": "email", - "maxLength": 254 - } - }, - "required": [ - "invite_email" - ] - }, - "ReferralOutcome": { - "type": "object", - "properties": { - "state": { - "type": "string" - } - }, - "required": [ - "state" - ] - }, - "RefreshResponse": { - "type": "object", - "properties": { - "access": { - "type": "string" - } - }, - "required": [ - "access" - ] - }, - "Refund": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true + "cadence": { + "$ref": "#/components/schemas/CadenceEnum" }, - "amount": { + "next_run_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true + "format": "date-time" }, - "currency": { + "last_run_at": { "type": "string", - "readOnly": true - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/RefundStatusEnum" - } - ], - "readOnly": true - }, - "provider": { - "allOf": [ - { - "$ref": "#/components/schemas/RefundProviderEnum" - } - ], - "readOnly": true + "format": "date-time", + "readOnly": true, + "nullable": true }, - "reason": { - "type": "string", - "readOnly": true + "is_active": { + "type": "boolean" }, - "method": { - "allOf": [ - { - "$ref": "#/components/schemas/Method2edEnum" - } - ], - "readOnly": true + "destination": { + "type": "integer", + "nullable": true }, - "reference": { - "type": "string", - "readOnly": true + "recipient_emails": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "maxItems": 10 }, - "refunded_at": { - "type": "string", - "format": "date-time", + "created_by": { + "type": "integer", "readOnly": true, "nullable": true }, @@ -99310,650 +122358,709 @@ "format": "date-time", "readOnly": true }, - "settled_at": { + "updated_at": { "type": "string", "format": "date-time", - "readOnly": true, - "nullable": true - }, - "amends": { - "type": "integer", - "readOnly": true, - "nullable": true + "readOnly": true }, - "corrected": { - "type": "boolean", + "last_delivery": { + "allOf": [ + { + "$ref": "#/components/schemas/ReportDelivery" + } + ], + "nullable": true, "readOnly": true + }, + "condition": { + "$ref": "#/components/schemas/ReportCondition" + }, + "recipient_roles": { + "type": "array", + "items": { + "type": "string", + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "maxItems": 20 + }, + "recipient_actions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RecipientActionsEnum" + }, + "maxItems": 20 } }, "required": [ - "amends", - "amount", - "corrected", "created_at", - "currency", + "created_by", "id", - "method", - "provider", - "reason", - "reference", - "refunded_at", - "settled_at", - "status" + "last_delivery", + "last_run_at", + "makerspace", + "report_key", + "updated_at" ] }, - "RefundProviderEnum": { - "enum": [ - "stripe", - "razorpay", - "manual" - ], - "type": "string", - "description": "* `stripe` - Stripe\n* `razorpay` - Razorpay\n* `manual` - Manual" + "ReportScheduleFilters": { + "type": "object", + "description": "The subset of manual-export query parameters a schedule may pin.\n\n`window_days` is the recurring-report shape (\"the last 7 days, every Monday\"); fixed\n`start`/`end` reproduce the same file each run and are mutually exclusive with it.", + "properties": { + "start": { + "type": "string", + "format": "date" + }, + "end": { + "type": "string", + "format": "date" + }, + "window_days": { + "type": "integer", + "maximum": 366, + "minimum": 1 + }, + "status": { + "$ref": "#/components/schemas/Status66aEnum" + }, + "subject_type": { + "$ref": "#/components/schemas/SubjectTypeEnum" + }, + "months": { + "type": "integer", + "maximum": 120, + "minimum": 1 + }, + "subject_kind": { + "type": "string", + "maxLength": 48 + }, + "subject_id": { + "type": "integer", + "minimum": 1 + } + } }, - "RefundStatusEnum": { + "ReportScheduleFormatEnum": { "enum": [ - "pending", - "succeeded", - "failed" + "csv", + "xlsx" ], "type": "string", - "description": "* `pending` - Pending\n* `succeeded` - Succeeded\n* `failed` - Failed" - }, - "Rehydrate": { - "type": "object", - "properties": { - "destination": { - "type": "integer" - } - }, - "required": [ - "destination" - ] + "description": "* `csv` - CSV\n* `xlsx` - XLSX" }, - "RejectRequest": { + "ReportViewState": { "type": "object", "properties": { - "reason": { - "type": "string" + "report_key": { + "$ref": "#/components/schemas/ReportKeyEnum" + }, + "filters": { + "$ref": "#/components/schemas/ReportScheduleFilters" + }, + "grain": { + "type": "string", + "maxLength": 8 + }, + "columns": { + "type": "array", + "items": { + "type": "string", + "maxLength": 80 + }, + "maxItems": 100 } }, "required": [ - "reason" + "report_key" ] }, - "RejoinDuesRuleEnum": { - "enum": [ - "once", - "each_join" - ], - "type": "string", - "description": "* `once` - Joining dues once\n* `each_join` - Joining dues on each join" - }, - "RelationshipEnum": { + "RequestAccessEnum": { "enum": [ - "owner", - "manager", - "affiliate" + "anyone" ], "type": "string", - "description": "* `owner` - Owner\n* `manager` - Manager\n* `affiliate` - Affiliate" + "description": "* `anyone` - anyone" }, - "ReorderDraft": { + "RequestItemInput": { "type": "object", "properties": { "product_id": { + "type": "integer" + }, + "quantity": { "type": "integer", + "maximum": 99, "minimum": 1 } }, "required": [ - "product_id" + "product_id", + "quantity" ] }, - "ReorderSuggestion": { + "RequestSubmit": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "name": { - "type": "string", - "readOnly": true - }, - "manufacturer": { + "website": { "type": "string", - "readOnly": true + "writeOnly": true }, - "model": { + "contact_name": { "type": "string", - "readOnly": true + "description": "Required for an account-less submission.", + "maxLength": 200 }, - "sku": { - "type": "string", - "readOnly": true + "contact_email": { + "description": "Required for an account-less submission; normalized to lowercase.", + "oneOf": [ + { + "type": "string", + "format": "email", + "maxLength": 254 + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "barcode": { + "contact_phone": { "type": "string", - "readOnly": true + "maxLength": 32 }, - "supplier": { + "requested_for": { "type": "string", - "readOnly": true - }, - "available_quantity": { - "type": "integer", - "readOnly": true - }, - "reorder_point": { - "type": "integer", - "readOnly": true + "default": "", + "maxLength": 500 }, - "reorder_quantity": { - "type": "integer", - "readOnly": true + "items": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RequestItemInput" + }, + "maxItems": 20 } }, "required": [ - "available_quantity", - "barcode", - "id", - "manufacturer", - "model", - "name", - "reorder_point", - "reorder_quantity", - "sku", - "supplier" + "items" ] }, - "ReplayDirectHandout": { + "RequestSubmitResponse": { "type": "object", "properties": { - "kit_checklist": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 - }, - "borrower_id": { - "type": "integer" - }, - "remark": { - "type": "string" - }, - "container_id": { - "type": "integer", - "nullable": true - }, - "qr_payloads": { - "type": "array", - "items": { - "type": "string", - "maxLength": 64 - }, - "maxItems": 50 + "public_token": { + "type": "string", + "format": "uuid", + "readOnly": true }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/DirectLoanItem" - } + "status": { + "type": "string", + "readOnly": true } }, "required": [ - "borrower_id" + "public_token", + "status" ] }, - "ReplayDirectReturn": { + "RequestTimelineResponse": { "type": "object", "properties": { - "loan_id": { - "type": "integer", - "minimum": 1 - }, - "kit_checklist": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 + "request_id": { + "type": "integer" }, - "notes": { - "type": "string" + "limit": { + "type": "integer" }, - "qr_payload": { - "type": "string", - "maxLength": 64 + "truncated": { + "type": "boolean" }, - "resolutions": { + "events": { "type": "array", "items": { - "$ref": "#/components/schemas/ReturnItemResolution" + "$ref": "#/components/schemas/TimelineEvent" } } }, "required": [ - "notes" + "events", + "limit", + "request_id", + "truncated" ] }, - "ReplayDomainPayload": { + "ResetPasswordConfirmError": { "oneOf": [ { - "$ref": "#/components/schemas/KioskAssignBox" - }, - { - "$ref": "#/components/schemas/ReplayReviewedIssue" + "$ref": "#/components/schemas/PasswordResetFailure" }, { - "$ref": "#/components/schemas/ReplayReviewedReturn" - }, + "$ref": "#/components/schemas/PasswordValidationFailure" + } + ] + }, + "ResetPasswordConfirmRequest": { + "oneOf": [ { - "$ref": "#/components/schemas/ReplayDirectHandout" + "$ref": "#/components/schemas/OtpResetPasswordConfirm" }, { - "$ref": "#/components/schemas/ReplayDirectReturn" + "$ref": "#/components/schemas/LegacyResetPasswordConfirm" } ] }, - "ReplayEnvelope": { + "ResetPasswordRequest": { "type": "object", "properties": { - "client_command_id": { - "type": "string", - "format": "uuid" - }, - "kind": { - "$ref": "#/components/schemas/ReplayEnvelopeKindEnum" - }, - "version": { - "type": "integer", - "minimum": 1 - }, - "aggregate_id": { - "type": "string", - "format": "uuid" - }, - "sequence": { - "type": "integer", - "maximum": 10000, - "minimum": 1 - }, - "predecessor_client_id": { - "type": "string", - "format": "uuid", - "nullable": true - }, - "client_created_at": { + "password": { "type": "string", - "format": "date-time" - }, - "payload": { - "$ref": "#/components/schemas/ReplayDomainPayload" + "writeOnly": true, + "minLength": 8 + } + } + }, + "ResetPasswordResponse": { + "type": "object", + "properties": { + "username": { + "type": "string" }, - "evidence": { - "allOf": [ - { - "$ref": "#/components/schemas/KioskEvidencePrepare" - } - ], - "nullable": true + "temporary_password": { + "type": "string" } }, "required": [ - "aggregate_id", - "client_command_id", - "client_created_at", - "kind", - "payload", - "sequence", - "version" + "temporary_password", + "username" ] }, - "ReplayEnvelopeKindEnum": { + "ResolutionCodeEnum": { "enum": [ - "assign_box", - "reviewed_issue", - "reviewed_return", - "direct_handout", - "direct_return" + "acknowledged_no_change", + "replaced_online" ], "type": "string", - "description": "* `assign_box` - assign_box\n* `reviewed_issue` - reviewed_issue\n* `reviewed_return` - reviewed_return\n* `direct_handout` - direct_handout\n* `direct_return` - direct_return" + "description": "* `acknowledged_no_change` - Acknowledged No Change\n* `replaced_online` - Replaced Online" }, - "ReplayRecovery": { + "ResponseResultEnum": { + "enum": [ + "pending", + "succeeded", + "retryable_failed", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `succeeded` - Succeeded\n* `retryable_failed` - Retryable failure\n* `failed` - Failed" + }, + "RestoreCreate": { "type": "object", "properties": { - "id": { - "type": "string", - "format": "uuid", - "readOnly": true - }, - "makerspace_id": { - "type": "integer", - "readOnly": true - }, - "station_id": { - "type": "integer", - "readOnly": true - }, - "station_name": { - "type": "string" - }, - "station_kind": { - "type": "string" - }, - "operator_display": { - "type": "string", - "readOnly": true - }, - "operation_kind": { - "type": "string", - "maxLength": 24 - }, - "operation_version": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "aggregate_id": { + "archive": { "type": "string", "format": "uuid" }, - "sequence": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "state": { - "$ref": "#/components/schemas/ReplayRecoveryStateEnum" - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "client_created_at": { - "type": "string", - "format": "date-time" - }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true - }, - "expires_at": { - "type": "string", - "format": "date-time" - }, - "retained_until": { + "kind": { + "$ref": "#/components/schemas/KindB02Enum" + } + }, + "required": [ + "archive", + "kind" + ] + }, + "RestoreDecision": { + "type": "object", + "properties": { + "decision": { + "$ref": "#/components/schemas/RestoreDecisionDecisionEnum" + } + }, + "required": [ + "decision" + ] + }, + "RestoreDecisionDecisionEnum": { + "enum": [ + "proceed", + "reset", + "abort" + ], + "type": "string", + "description": "* `proceed` - proceed\n* `reset` - reset\n* `abort` - abort" + }, + "RestoreOperation": { + "type": "object", + "properties": { + "id": { "type": "string", + "format": "uuid", "readOnly": true }, - "failure_code": { - "type": "string", - "nullable": true, - "maxLength": 64 - }, - "failure_detail": { + "archive": { "type": "string", - "nullable": true, - "maxLength": 200 - }, - "subject_request_id": { - "type": "integer", - "nullable": true - }, - "subject_loan_id": { - "type": "integer", - "nullable": true - }, - "recovery_request_id": { - "type": "integer", - "nullable": true, + "format": "uuid", "readOnly": true }, - "recovery_loan_id": { - "type": "integer", - "nullable": true, + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/KindB02Enum" + } + ], "readOnly": true }, - "result_request_id": { - "type": "integer", - "nullable": true + "stage": { + "allOf": [ + { + "$ref": "#/components/schemas/StageEnum" + } + ], + "readOnly": true }, - "result_loan_id": { - "type": "integer", - "nullable": true + "decision": { + "allOf": [ + { + "$ref": "#/components/schemas/RestoreOperationDecisionEnum" + } + ], + "readOnly": true }, - "result_return_event_id": { - "type": "integer", - "nullable": true + "restore_diff": { + "readOnly": true }, - "predecessor_id": { + "decision_deadline_at": { "type": "string", - "format": "uuid", + "format": "date-time", + "readOnly": true, "nullable": true }, - "predecessor_state": { + "supervisor_heartbeat_at": { "type": "string", + "format": "date-time", + "readOnly": true, "nullable": true }, - "evidence_id": { - "type": "integer", - "nullable": true + "error_detail": { + "type": "string", + "readOnly": true }, - "can_view_evidence": { - "type": "boolean", + "requested_by_username_snapshot": { + "type": "string", "readOnly": true }, - "resolution_code": { - "nullable": true, - "oneOf": [ - { - "$ref": "#/components/schemas/ResolutionCodeEnum" - }, - { - "$ref": "#/components/schemas/BlankEnum" - }, - { - "$ref": "#/components/schemas/NullEnum" - } - ] + "requested_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "resolved_at": { + "completed_at": { "type": "string", "format": "date-time", + "readOnly": true, "nullable": true }, - "resolved_by_display": { + "updated_at": { "type": "string", - "nullable": true, + "format": "date-time", "readOnly": true - }, - "replacement_command_id": { - "type": "string", - "format": "uuid", - "nullable": true } }, "required": [ - "aggregate_id", - "can_view_evidence", - "client_created_at", - "created_at", - "evidence_id", - "expires_at", + "archive", + "completed_at", + "decision", + "decision_deadline_at", + "error_detail", "id", - "makerspace_id", - "operation_kind", - "operation_version", - "operator_display", - "predecessor_id", - "recovery_loan_id", - "recovery_request_id", - "replacement_command_id", - "resolved_by_display", - "result_loan_id", - "result_request_id", - "result_return_event_id", - "retained_until", - "sequence", - "station_id", - "station_kind", - "station_name", - "subject_loan_id", - "subject_request_id" + "kind", + "requested_at", + "requested_by_username_snapshot", + "restore_diff", + "stage", + "supervisor_heartbeat_at", + "updated_at" ] }, - "ReplayRecoveryStateEnum": { + "RestoreOperationDecisionEnum": { "enum": [ - "prepared", - "claimed", - "succeeded", - "failed" + "pending", + "proceed", + "reset", + "abort" ], "type": "string", - "description": "* `prepared` - Prepared\n* `claimed` - Claimed\n* `succeeded` - Succeeded\n* `failed` - Failed" + "description": "* `pending` - Pending\n* `proceed` - Proceed\n* `reset` - Proceed with authority reset\n* `abort` - Abort" }, - "ReplayResolution": { + "RestrictUser": { "type": "object", "properties": { - "resolution_code": { - "$ref": "#/components/schemas/ResolutionCodeEnum" + "reason": { + "type": "string" }, - "replacement_command_id": { - "type": "string", - "format": "uuid", - "nullable": true + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/RestrictUserStatusEnum" + } + ], + "default": "restricted" } }, "required": [ - "resolution_code" + "reason" ] }, - "ReplayResult": { + "RestrictUserStatusEnum": { + "enum": [ + "restricted", + "suspended" + ], + "type": "string", + "description": "* `restricted` - restricted\n* `suspended` - suspended" + }, + "Result": { "type": "object", "properties": { - "replay_id": { + "operation_id": { "type": "string", "format": "uuid" }, - "client_command_id": { + "external_job_id": { "type": "string", - "format": "uuid" - }, - "state": { - "$ref": "#/components/schemas/ReplayResultStateEnum" - }, - "kind": { - "type": "string" + "maxLength": 120 }, - "expires_at": { - "type": "string", - "format": "date-time" + "outcome": { + "$ref": "#/components/schemas/ResultOutcomeEnum" }, - "evidence_id": { + "actual_minutes": { "type": "integer", - "nullable": true + "maximum": 2147483647, + "minimum": 0 }, - "result_request_id": { - "type": "integer", - "nullable": true + "consumptions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Consumption" + } }, - "result_loan_id": { - "type": "integer", - "nullable": true + "failure_reason": { + "type": "string", + "default": "", + "maxLength": 1000 }, - "result_return_event_id": { + "percent_complete": { "type": "integer", + "maximum": 100, + "minimum": 0, "nullable": true }, - "failure_code": { + "actual_grams": { "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "nullable": true }, - "failure_detail": { + "actual_quantity": { "type": "string", - "nullable": true - }, - "upload": { - "allOf": [ - { - "$ref": "#/components/schemas/EvidenceUrlResponse" - } - ], + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "nullable": true } }, "required": [ - "client_command_id", - "evidence_id", - "expires_at", - "failure_code", - "failure_detail", - "kind", - "replay_id", - "result_loan_id", - "result_request_id", - "result_return_event_id", - "state" + "actual_minutes", + "consumptions", + "external_job_id", + "operation_id", + "outcome" ] }, - "ReplayResultStateEnum": { + "ResultCodeEnum": { "enum": [ - "prepared", - "claimed", + "ok", + "out_of_labels", + "paper_empty", + "offline", + "printer_error", + "unknown_error" + ], + "type": "string", + "description": "* `ok` - ok\n* `out_of_labels` - out_of_labels\n* `paper_empty` - paper_empty\n* `offline` - offline\n* `printer_error` - printer_error\n* `unknown_error` - unknown_error" + }, + "ResultEnum": { + "enum": [ + "pending", "succeeded", + "retryable_failed", "failed" ], "type": "string", - "description": "* `prepared` - prepared\n* `claimed` - claimed\n* `succeeded` - succeeded\n* `failed` - failed" + "description": "* `pending` - Pending\n* `succeeded` - Succeeded\n* `retryable_failed` - Retryable failure\n* `failed` - Failed" }, - "ReplayReviewedIssue": { + "ResultOutcomeEnum": { + "enum": [ + "completed", + "failed" + ], + "type": "string", + "description": "* `completed` - completed\n* `failed` - failed" + }, + "Retry": { "type": "object", "properties": { - "request_id": { + "generation": { "type": "integer", - "minimum": 1 - }, - "kit_checklist": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 + "minimum": 0 + } + }, + "required": [ + "generation" + ] + }, + "ReturnAssetResolution": { + "type": "object", + "properties": { + "asset_id": { + "type": "integer" }, - "remark": { + "outcome": { + "$ref": "#/components/schemas/ReturnAssetResolutionOutcomeEnum" + } + }, + "required": [ + "asset_id", + "outcome" + ] + }, + "ReturnAssetResolutionOutcomeEnum": { + "enum": [ + "returned", + "damaged", + "missing" + ], + "type": "string", + "description": "* `returned` - returned\n* `damaged` - damaged\n* `missing` - missing" + }, + "ReturnDue": { + "type": "object", + "properties": { + "return_due_at": { "type": "string", - "default": "" + "format": "date-time", + "nullable": true + } + }, + "required": [ + "return_due_at" + ] + }, + "ReturnItemResolution": { + "type": "object", + "properties": { + "item_id": { + "type": "integer" }, - "asset_qr_payloads": { - "type": "array", - "items": { - "type": "string" - } + "returned": { + "type": "integer", + "minimum": 0, + "default": 0 }, - "rejects": { + "damaged": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "missing": { + "type": "integer", + "minimum": 0, + "default": 0 + }, + "assets": { "type": "array", "items": { - "$ref": "#/components/schemas/IssueReject" + "$ref": "#/components/schemas/ReturnAssetResolution" } } }, "required": [ - "request_id" + "item_id" ] }, - "ReplayReviewedReturn": { + "ReturnPolicy": { "type": "object", "properties": { - "request_id": { + "id": { + "type": "integer", + "readOnly": true + }, + "default_loan_days": { + "type": "integer", + "maximum": 365, + "minimum": 1 + }, + "loan_renewal_mode": { + "$ref": "#/components/schemas/LoanRenewalModeEnum" + }, + "loan_max_renewals": { + "type": "integer", + "maximum": 100, + "minimum": 0 + }, + "loan_renewal_days": { "type": "integer", + "maximum": 365, "minimum": 1 }, + "loan_max_active": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "loan_max_items": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "loan_reminder_offsets_hours": { + "type": "array", + "items": { + "type": "integer", + "minimum": -8760, + "maximum": 8760 + }, + "maxItems": 100, + "uniqueItems": true, + "description": "Signed whole hours from due time: negative before, zero due, positive overdue." + }, + "loan_escalate_overdue_days": { + "type": "integer", + "maximum": 365, + "minimum": 0 + } + }, + "required": [ + "id" + ] + }, + "ReturnReminderCronResponse": { + "type": "object", + "properties": { + "sent": { + "type": "integer" + }, + "skipped": { + "type": "integer" + } + }, + "required": [ + "sent", + "skipped" + ] + }, + "ReturnRequest": { + "type": "object", + "properties": { "kit_checklist": { "type": "array", "items": { @@ -99961,6 +123068,9 @@ }, "maxItems": 3550 }, + "evidence_id": { + "type": "integer" + }, "box_code": { "type": "string" }, @@ -99976,151 +123086,212 @@ }, "required": [ "box_code", + "evidence_id", "remark", - "request_id", "resolutions" ] }, - "ReportCatalog": { + "ReturnsReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ReturnsReportRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "ReturnsReportRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "requester": { + "type": "string" + }, + "status": { + "type": "string" + }, + "closed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "count": { + "type": "integer", + "maximum": 1, + "minimum": 1, + "description": "One loan per detail row; sum to count closed loans in this result." + } + }, + "required": [ + "closed_at", + "count", + "id", + "requester", + "status" + ] + }, + "RevenueOverTimeReport": { "type": "object", "properties": { - "results": { + "rows": { "type": "array", "items": { - "$ref": "#/components/schemas/ReportCatalogItem" + "type": "array", + "items": {} + } + }, + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RevenuePeriod" } } }, "required": [ - "results" + "rows", + "typed_rows" ] }, - "ReportCatalogItem": { + "RevenuePeriod": { "type": "object", "properties": { - "key": { - "type": "string" + "makerspace_id": { + "type": "integer" }, - "title": { + "currency": { "type": "string" }, - "fields": { - "type": "array", - "items": { - "type": "string" - } - }, - "exportable": { - "type": "boolean" - }, - "summary": { - "type": "boolean" - }, - "required_modules": { - "type": "array", - "items": { - "type": "string" - } - }, - "available": { - "type": "boolean", - "nullable": true + "collected_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "unavailable_reason": { + "refunded_amount": { "type": "string", - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "grains": { - "type": "array", - "items": { - "type": "string" - } + "period": { + "type": "string", + "format": "date" }, - "chart_hint": { + "subject_type": { "type": "string" }, - "aggregate_supported": { - "type": "boolean" - }, - "allowed_formats": { - "type": "array", - "items": { - "$ref": "#/components/schemas/AllowedFormatsEnum" - } + "rail": { + "$ref": "#/components/schemas/RailEnum" }, - "schedulable": { - "type": "boolean" + "owed_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "previewable": { - "type": "boolean" + "waived_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" }, - "condition_metrics": { - "type": "array", - "items": { - "type": "string" - } + "net_amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" } }, "required": [ - "aggregate_supported", - "allowed_formats", - "available", - "chart_hint", - "condition_metrics", - "exportable", - "fields", - "grains", - "key", - "previewable", - "required_modules", - "schedulable", - "summary", - "title", - "unavailable_reason" + "collected_amount", + "currency", + "net_amount", + "owed_amount", + "period", + "rail", + "refunded_amount", + "subject_type", + "waived_amount" ] }, - "ReportCondition": { + "Revoke": { "type": "object", "properties": { - "non_empty": { - "type": "boolean" - }, - "metric": { - "type": "string", - "description": "Sum this numeric column across the matching report rows.", - "maxLength": 80 - }, - "operator": { - "$ref": "#/components/schemas/OperatorEnum" - }, - "value": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,14}(?:\\.\\d{0,6})?$" - }, - "currency": { - "type": "string", - "pattern": "^[a-z]{3}$" + "reason": { + "type": "string" } } }, - "ReportDelivery": { + "Role": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "status": { - "allOf": [ + "makerspace_id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + }, + "slug": { + "type": "string", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "granted_actions": { + "type": "array", + "items": { + "type": "string" + }, + "readOnly": true + }, + "legacy_role": { + "readOnly": true, + "nullable": true, + "oneOf": [ { - "$ref": "#/components/schemas/ReportDeliveryStatusEnum" + "$ref": "#/components/schemas/LegacyRoleEnum" + }, + { + "$ref": "#/components/schemas/NullEnum" } - ], + ] + }, + "is_default": { + "type": "boolean", "readOnly": true }, - "error": { - "type": "string", + "is_protected": { + "type": "boolean", + "readOnly": true + }, + "combinable": { + "type": "boolean", + "readOnly": true + }, + "exclusive": { + "type": "boolean", + "readOnly": true + }, + "member_count": { + "type": "integer", "readOnly": true }, "created_at": { @@ -100128,92 +123299,76 @@ "format": "date-time", "readOnly": true }, - "expires_at": { + "updated_at": { "type": "string", "format": "date-time", - "readOnly": true, - "nullable": true - }, - "download_url": { - "type": "string", - "format": "uri", "readOnly": true } }, "required": [ + "combinable", "created_at", - "download_url", - "error", - "expires_at", + "exclusive", + "granted_actions", "id", - "status" + "is_default", + "is_protected", + "legacy_role", + "makerspace_id", + "member_count", + "name", + "slug", + "updated_at" ] }, - "ReportDeliveryStatusEnum": { + "RoleCddEnum": { "enum": [ - "sent", - "failed" + "space_manager", + "inventory_manager", + "print_manager", + "machine_manager", + "event_manager", + "booking_manager", + "handover", + "custom" ], "type": "string", - "description": "* `sent` - Sent\n* `failed` - Failed" + "description": "* `space_manager` - Space Manager\n* `inventory_manager` - Inventory Manager\n* `print_manager` - Print Manager\n* `machine_manager` - Machine Manager\n* `event_manager` - Event Manager\n* `booking_manager` - Booking Manager\n* `handover` - Handover\n* `custom` - Custom" }, - "ReportError": { + "RoleId": { "type": "object", "properties": { - "detail": { - "type": "string" - }, - "code": { - "type": "string" + "role_id": { + "type": "integer" } }, "required": [ - "detail" + "role_id" ] }, - "ReportKeyEnum": { + "RoleSlugEnum": { "enum": [ - "payment-cash-book", - "summary", - "taken-items", - "active-loans", - "returns", - "damaged-missing", - "damaged-lost", - "qr-scans", - "most-lent", - "top-borrowers", - "recently-added", - "machine-usage", - "event-attendance", - "booking-utilization", - "maintenance-activity", - "member-activity", - "machine-service", - "printer-service", - "fablab-health", - "payment-reconciliation", - "loan-throughput", - "inventory-control", - "evidence-compliance", - "import-quality", - "procurement-performance", - "communications-health", - "community-engagement", - "certification-coverage", - "module-operational-health", - "audit-trail", - "member-retention", - "membership-revenue", - "payments-ledger", - "revenue-over-time", - "cash-book", - "accounting-journal" + "space_manager", + "inventory_manager", + "machine_manager", + "event_manager", + "booking_manager", + "handover" ], "type": "string", - "description": "* `payment-cash-book` - payment-cash-book\n* `summary` - summary\n* `taken-items` - taken-items\n* `active-loans` - active-loans\n* `returns` - returns\n* `damaged-missing` - damaged-missing\n* `damaged-lost` - damaged-lost\n* `qr-scans` - qr-scans\n* `most-lent` - most-lent\n* `top-borrowers` - top-borrowers\n* `recently-added` - recently-added\n* `machine-usage` - machine-usage\n* `event-attendance` - event-attendance\n* `booking-utilization` - booking-utilization\n* `maintenance-activity` - maintenance-activity\n* `member-activity` - member-activity\n* `machine-service` - machine-service\n* `printer-service` - printer-service\n* `fablab-health` - fablab-health\n* `payment-reconciliation` - payment-reconciliation\n* `loan-throughput` - loan-throughput\n* `inventory-control` - inventory-control\n* `evidence-compliance` - evidence-compliance\n* `import-quality` - import-quality\n* `procurement-performance` - procurement-performance\n* `communications-health` - communications-health\n* `community-engagement` - community-engagement\n* `certification-coverage` - certification-coverage\n* `module-operational-health` - module-operational-health\n* `audit-trail` - audit-trail\n* `member-retention` - member-retention\n* `membership-revenue` - membership-revenue\n* `payments-ledger` - payments-ledger\n* `revenue-over-time` - revenue-over-time\n* `cash-book` - cash-book\n* `accounting-journal` - accounting-journal" + "description": "* `space_manager` - space_manager\n* `inventory_manager` - inventory_manager\n* `machine_manager` - machine_manager\n* `event_manager` - event_manager\n* `booking_manager` - booking_manager\n* `handover` - handover" }, - "ReportSchedule": { + "RowKindEnum": { + "enum": [ + "status", + "machine", + "consumption", + "failure" + ], + "type": "string", + "description": "* `status` - status\n* `machine` - machine\n* `consumption` - consumption\n* `failure` - failure" + }, + "SavedReportView": { "type": "object", "properties": { "id": { @@ -100224,52 +123379,20 @@ "type": "integer", "readOnly": true }, - "report_key": { - "$ref": "#/components/schemas/ReportKeyEnum" - }, - "filters": { - "$ref": "#/components/schemas/ReportScheduleFilters" - }, - "grain": { - "type": "string", - "maxLength": 8 - }, - "format": { - "$ref": "#/components/schemas/ReportScheduleFormatEnum" - }, - "cadence": { - "$ref": "#/components/schemas/CadenceEnum" + "owner": { + "type": "integer", + "readOnly": true }, - "next_run_at": { + "name": { "type": "string", - "format": "date-time" + "maxLength": 80 }, - "last_run_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "state": { + "$ref": "#/components/schemas/ReportViewState" }, - "is_active": { + "shared": { "type": "boolean" }, - "destination": { - "type": "integer", - "nullable": true - }, - "recipient_emails": { - "type": "array", - "items": { - "type": "string", - "format": "email" - }, - "maxItems": 10 - }, - "created_by": { - "type": "integer", - "readOnly": true, - "nullable": true - }, "created_at": { "type": "string", "format": "date-time", @@ -100279,778 +123402,762 @@ "type": "string", "format": "date-time", "readOnly": true - }, - "last_delivery": { - "allOf": [ - { - "$ref": "#/components/schemas/ReportDelivery" - } - ], - "nullable": true, - "readOnly": true - }, - "condition": { - "$ref": "#/components/schemas/ReportCondition" - }, - "recipient_roles": { - "type": "array", - "items": { - "type": "string", - "maxLength": 80, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "maxItems": 20 - }, - "recipient_actions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RecipientActionsEnum" - }, - "maxItems": 20 } }, "required": [ "created_at", - "created_by", "id", - "last_delivery", - "last_run_at", "makerspace", - "report_key", + "name", + "owner", + "state", "updated_at" ] }, - "ReportScheduleFilters": { + "SavedReportViewList": { "type": "object", - "description": "The subset of manual-export query parameters a schedule may pin.\n\n`window_days` is the recurring-report shape (\"the last 7 days, every Monday\"); fixed\n`start`/`end` reproduce the same file each run and are mutually exclusive with it.", "properties": { - "start": { - "type": "string", - "format": "date" + "count": { + "type": "integer" }, - "end": { + "next": { "type": "string", - "format": "date" - }, - "window_days": { - "type": "integer", - "maximum": 366, - "minimum": 1 - }, - "status": { - "$ref": "#/components/schemas/Status66aEnum" - }, - "subject_type": { - "$ref": "#/components/schemas/SubjectTypeEnum" - }, - "months": { - "type": "integer", - "maximum": 120, - "minimum": 1 - } - } - }, - "ReportScheduleFormatEnum": { - "enum": [ - "csv", - "xlsx" - ], - "type": "string", - "description": "* `csv` - CSV\n* `xlsx` - XLSX" - }, - "ReportViewState": { - "type": "object", - "properties": { - "report_key": { - "$ref": "#/components/schemas/ReportKeyEnum" - }, - "filters": { - "$ref": "#/components/schemas/ReportScheduleFilters" + "format": "uri", + "nullable": true }, - "grain": { + "previous": { "type": "string", - "maxLength": 8 + "format": "uri", + "nullable": true }, - "columns": { + "results": { "type": "array", "items": { - "type": "string", - "maxLength": 80 - }, - "maxItems": 100 + "$ref": "#/components/schemas/SavedReportView" + } } }, "required": [ - "report_key" + "count", + "next", + "previous", + "results" ] }, - "RequestAccessEnum": { - "enum": [ - "anyone" - ], - "type": "string", - "description": "* `anyone` - anyone" - }, - "RequestItemInput": { + "SavedView": { "type": "object", "properties": { - "product_id": { - "type": "integer" - }, - "quantity": { + "id": { "type": "integer", - "maximum": 99, - "minimum": 1 - } - }, - "required": [ - "product_id", - "quantity" - ] - }, - "RequestSubmit": { - "type": "object", - "properties": { - "website": { - "type": "string", - "writeOnly": true + "readOnly": true }, - "contact_name": { - "type": "string", - "description": "Required for an account-less submission.", - "maxLength": 200 + "makerspace": { + "type": "integer", + "readOnly": true }, - "contact_email": { - "description": "Required for an account-less submission; normalized to lowercase.", - "oneOf": [ - { - "type": "string", - "format": "email", - "maxLength": 254 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "owner": { + "type": "integer", + "readOnly": true }, - "contact_phone": { + "owner_username": { "type": "string", - "maxLength": 32 + "readOnly": true }, - "requested_for": { + "name": { "type": "string", - "default": "", - "maxLength": 500 + "maxLength": 80 }, - "items": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RequestItemInput" - }, - "maxItems": 20 - } - }, - "required": [ - "items" - ] - }, - "RequestSubmitResponse": { - "type": "object", - "properties": { - "public_token": { + "entity": { + "$ref": "#/components/schemas/SavedViewEntityEnum" + }, + "state": { + "type": "object", + "additionalProperties": { + "type": "string", + "maxLength": 250 + } + }, + "shared": { + "type": "boolean" + }, + "created_at": { "type": "string", - "format": "uuid", + "format": "date-time", "readOnly": true }, - "status": { + "updated_at": { "type": "string", + "format": "date-time", "readOnly": true } }, "required": [ - "public_token", - "status" + "created_at", + "entity", + "id", + "makerspace", + "name", + "owner", + "owner_username", + "state", + "updated_at" ] }, - "RequestTimelineResponse": { + "SavedViewEntityEnum": { + "enum": [ + "requests", + "inventory", + "members", + "payments", + "bookings", + "events", + "audit" + ], + "type": "string", + "description": "* `requests` - requests\n* `inventory` - inventory\n* `members` - members\n* `payments` - payments\n* `bookings` - bookings\n* `events` - events\n* `audit` - audit" + }, + "SavedViewList": { "type": "object", "properties": { - "request_id": { + "count": { "type": "integer" }, - "limit": { - "type": "integer" + "next": { + "type": "string", + "format": "uri", + "nullable": true }, - "truncated": { - "type": "boolean" + "previous": { + "type": "string", + "format": "uri", + "nullable": true }, - "events": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/TimelineEvent" + "$ref": "#/components/schemas/SavedView" } } }, "required": [ - "events", - "limit", - "request_id", - "truncated" + "count", + "next", + "previous", + "results" ] }, - "ResetPasswordConfirmError": { - "oneOf": [ - { - "$ref": "#/components/schemas/PasswordResetFailure" + "Schedule": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true }, - { - "$ref": "#/components/schemas/PasswordValidationFailure" - } - ] - }, - "ResetPasswordConfirmRequest": { - "oneOf": [ - { - "$ref": "#/components/schemas/OtpResetPasswordConfirm" + "name": { + "type": "string", + "maxLength": 200 }, - { - "$ref": "#/components/schemas/LegacyResetPasswordConfirm" + "container": { + "type": "integer", + "nullable": true + }, + "interval_days": { + "type": "integer", + "minimum": 1 + }, + "next_due": { + "type": "string", + "format": "date" + }, + "blind": { + "type": "boolean" + }, + "is_active": { + "type": "boolean" } + }, + "required": [ + "id", + "interval_days", + "name", + "next_due" ] }, - "ResetPasswordRequest": { - "type": "object", - "properties": { - "password": { - "type": "string", - "writeOnly": true, - "minLength": 8 - } - } + "ScopeEnum": { + "enum": [ + "deployment", + "makerspace" + ], + "type": "string", + "description": "* `deployment` - Full deployment\n* `makerspace` - Makerspace" }, - "ResetPasswordResponse": { + "ScopeModeEnum": { + "enum": [ + "machine", + "full", + "handover" + ], + "type": "string", + "description": "* `machine` - machine\n* `full` - full\n* `handover` - handover" + }, + "SearchHit": { "type": "object", "properties": { - "username": { + "entity": { + "$ref": "#/components/schemas/SearchHitEntityEnum" + }, + "id": { + "type": "integer" + }, + "label": { "type": "string" }, - "temporary_password": { + "tab": { "type": "string" + }, + "query": { + "type": "object", + "additionalProperties": { + "type": "string" + } } }, "required": [ - "temporary_password", - "username" + "entity", + "id", + "label", + "query", + "tab" ] }, - "ResolutionCodeEnum": { - "enum": [ - "acknowledged_no_change", - "replaced_online" - ], - "type": "string", - "description": "* `acknowledged_no_change` - Acknowledged No Change\n* `replaced_online` - Replaced Online" - }, - "ResponseResultEnum": { + "SearchHitEntityEnum": { "enum": [ - "pending", - "succeeded", - "retryable_failed", - "failed" + "requests", + "inventory", + "members", + "bookings", + "events", + "machines" ], "type": "string", - "description": "* `pending` - Pending\n* `succeeded` - Succeeded\n* `retryable_failed` - Retryable failure\n* `failed` - Failed" + "description": "* `requests` - requests\n* `inventory` - inventory\n* `members` - members\n* `bookings` - bookings\n* `events` - events\n* `machines` - machines" }, - "RestoreCreate": { + "SearchOutput": { "type": "object", "properties": { - "archive": { - "type": "string", - "format": "uuid" - }, - "kind": { - "$ref": "#/components/schemas/KindB02Enum" + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SearchHit" + } } }, "required": [ - "archive", - "kind" + "results" ] }, - "RestoreDecision": { + "SectionOrderEnum": { + "enum": [ + "HERO", + "ABOUT", + "HIGHLIGHTS", + "SCHEDULE", + "SPEAKERS", + "SPONSORS", + "VENUE", + "FAQ", + "RESOURCES" + ], + "type": "string", + "description": "* `HERO` - HERO\n* `ABOUT` - ABOUT\n* `HIGHLIGHTS` - HIGHLIGHTS\n* `SCHEDULE` - SCHEDULE\n* `SPEAKERS` - SPEAKERS\n* `SPONSORS` - SPONSORS\n* `VENUE` - VENUE\n* `FAQ` - FAQ\n* `RESOURCES` - RESOURCES" + }, + "SegmentChoice": { "type": "object", "properties": { - "decision": { - "$ref": "#/components/schemas/RestoreDecisionDecisionEnum" + "id": { + "type": "integer" + }, + "name": { + "type": "string" } }, "required": [ - "decision" + "id", + "name" ] }, - "RestoreDecisionDecisionEnum": { + "SegmentSourceEnum": { "enum": [ - "proceed", - "reset", - "abort" + "billed", + "estimated", + "not_applicable" ], "type": "string", - "description": "* `proceed` - proceed\n* `reset` - reset\n* `abort` - abort" + "description": "* `billed` - billed\n* `estimated` - estimated\n* `not_applicable` - not_applicable" }, - "RestoreOperation": { + "SeriesCollaborationInbox": { "type": "object", "properties": { "id": { - "type": "string", - "format": "uuid", + "type": "integer", "readOnly": true }, - "archive": { - "type": "string", - "format": "uuid", + "series_id": { + "type": "integer", "readOnly": true }, - "kind": { - "allOf": [ - { - "$ref": "#/components/schemas/KindB02Enum" - } - ], + "series_title": { + "type": "string", "readOnly": true }, - "stage": { - "allOf": [ - { - "$ref": "#/components/schemas/StageEnum" - } - ], + "host_name": { + "type": "string", "readOnly": true }, - "decision": { + "host_slug": { + "type": "string", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "status": { "allOf": [ { - "$ref": "#/components/schemas/RestoreOperationDecisionEnum" + "$ref": "#/components/schemas/StatusB9dEnum" } ], "readOnly": true }, - "restore_diff": { + "next_occurrence_at": { + "type": "string", + "format": "date-time", "readOnly": true }, - "decision_deadline_at": { + "created_at": { "type": "string", "format": "date-time", - "readOnly": true, - "nullable": true + "readOnly": true }, - "supervisor_heartbeat_at": { + "responded_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true + } + }, + "required": [ + "created_at", + "host_name", + "host_slug", + "id", + "next_occurrence_at", + "responded_at", + "series_id", + "series_title", + "status" + ] + }, + "SeriesCollaborationRespond": { + "type": "object", + "properties": { + "accept": { + "type": "boolean" + } + }, + "required": [ + "accept" + ] + }, + "SeriesCollaborator": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true }, - "error_detail": { - "type": "string", + "series_id": { + "type": "integer", "readOnly": true }, - "requested_by_username_snapshot": { - "type": "string", + "makerspace_id": { + "type": "integer", "readOnly": true }, - "requested_at": { + "makerspace_name": { "type": "string", - "format": "date-time", "readOnly": true }, - "completed_at": { + "makerspace_slug": { "type": "string", - "format": "date-time", + "readOnly": true, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/StatusB9dEnum" + } + ], + "readOnly": true + }, + "invited_by_id": { + "type": "integer", "readOnly": true, "nullable": true }, - "updated_at": { + "responded_by_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { "type": "string", "format": "date-time", "readOnly": true + }, + "responded_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true } }, "required": [ - "archive", - "completed_at", - "decision", - "decision_deadline_at", - "error_detail", + "created_at", "id", - "kind", - "requested_at", - "requested_by_username_snapshot", - "restore_diff", - "stage", - "supervisor_heartbeat_at", - "updated_at" + "invited_by_id", + "makerspace_id", + "makerspace_name", + "makerspace_slug", + "responded_at", + "responded_by_id", + "series_id", + "status" ] }, - "RestoreOperationDecisionEnum": { - "enum": [ - "pending", - "proceed", - "reset", - "abort" - ], - "type": "string", - "description": "* `pending` - Pending\n* `proceed` - Proceed\n* `reset` - Proceed with authority reset\n* `abort` - Abort" - }, - "RestrictUser": { + "SeriesCollaboratorReplace": { "type": "object", "properties": { - "reason": { - "type": "string" - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/RestrictUserStatusEnum" - } - ], - "default": "restricted" + "slugs": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[-a-zA-Z0-9_]+$" + } } }, "required": [ - "reason" + "slugs" ] }, - "RestrictUserStatusEnum": { - "enum": [ - "restricted", - "suspended" - ], - "type": "string", - "description": "* `restricted` - restricted\n* `suspended` - suspended" - }, - "Result": { + "ServiceAccept": { "type": "object", "properties": { - "operation_id": { - "type": "string", - "format": "uuid" - }, - "external_job_id": { - "type": "string", - "maxLength": 120 - }, - "outcome": { - "$ref": "#/components/schemas/ResultOutcomeEnum" - }, - "actual_minutes": { + "estimated_minutes": { "type": "integer", - "maximum": 2147483647, "minimum": 0 }, - "consumptions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Consumption" - } - }, - "failure_reason": { + "planned_grams": { "type": "string", - "default": "", - "maxLength": 1000 + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "percent_complete": { + "note": { + "type": "string" + } + } + }, + "ServiceComplete": { + "type": "object", + "properties": { + "actual_minutes": { "type": "integer", - "maximum": 100, - "minimum": 0, - "nullable": true + "minimum": 0 }, "actual_grams": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "nullable": true + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, "actual_quantity": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "nullable": true + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "consumptions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ServiceConsumptionInput" + } } }, "required": [ - "actual_minutes", - "consumptions", - "external_job_id", - "operation_id", - "outcome" + "actual_minutes" ] }, - "ResultCodeEnum": { - "enum": [ - "ok", - "out_of_labels", - "paper_empty", - "offline", - "printer_error", - "unknown_error" - ], - "type": "string", - "description": "* `ok` - ok\n* `out_of_labels` - out_of_labels\n* `paper_empty` - paper_empty\n* `offline` - offline\n* `printer_error` - printer_error\n* `unknown_error` - unknown_error" - }, - "ResultEnum": { - "enum": [ - "pending", - "succeeded", - "retryable_failed", - "failed" - ], - "type": "string", - "description": "* `pending` - Pending\n* `succeeded` - Succeeded\n* `retryable_failed` - Retryable failure\n* `failed` - Failed" - }, - "ResultOutcomeEnum": { - "enum": [ - "completed", - "failed" - ], - "type": "string", - "description": "* `completed` - completed\n* `failed` - failed" - }, - "Retry": { + "ServiceConsumption": { "type": "object", "properties": { - "generation": { + "id": { "type": "integer", - "minimum": 0 - } - }, - "required": [ - "generation" - ] - }, - "ReturnAssetResolution": { - "type": "object", - "properties": { - "asset_id": { - "type": "integer" + "readOnly": true + }, + "machine_consumable_id": { + "type": "integer", + "readOnly": true + }, + "measurement": { + "type": "string", + "readOnly": true + }, + "product_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "label": { + "type": "string", + "readOnly": true + }, + "quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true }, "outcome": { - "$ref": "#/components/schemas/ReturnAssetResolutionOutcomeEnum" + "type": "string", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ - "asset_id", - "outcome" + "created_at", + "id", + "label", + "machine_consumable_id", + "measurement", + "outcome", + "product_id", + "quantity" ] }, - "ReturnAssetResolutionOutcomeEnum": { - "enum": [ - "returned", - "damaged", - "missing" - ], - "type": "string", - "description": "* `returned` - returned\n* `damaged` - damaged\n* `missing` - missing" - }, - "ReturnDue": { + "ServiceConsumptionInput": { "type": "object", "properties": { - "return_due_at": { + "machine_consumable_id": { + "type": "integer", + "minimum": 1 + }, + "quantity": { "type": "string", - "format": "date-time", - "nullable": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" } }, "required": [ - "return_due_at" + "machine_consumable_id", + "quantity" ] }, - "ReturnItemResolution": { + "ServiceFail": { "type": "object", "properties": { - "item_id": { - "type": "integer" - }, - "returned": { + "actual_minutes": { "type": "integer", - "minimum": 0, - "default": 0 + "minimum": 0 }, - "damaged": { - "type": "integer", - "minimum": 0, - "default": 0 + "actual_grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "missing": { - "type": "integer", - "minimum": 0, - "default": 0 + "actual_quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "assets": { + "consumptions": { "type": "array", "items": { - "$ref": "#/components/schemas/ReturnAssetResolution" + "$ref": "#/components/schemas/ServiceConsumptionInput" } + }, + "reason": { + "type": "string" + }, + "percent_complete": { + "type": "integer", + "maximum": 100, + "minimum": 0 } }, "required": [ - "item_id" + "actual_minutes", + "percent_complete", + "reason" ] }, - "ReturnPolicy": { + "ServiceFile": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "default_loan_days": { - "type": "integer", - "maximum": 365, - "minimum": 1 - }, - "loan_renewal_mode": { - "$ref": "#/components/schemas/LoanRenewalModeEnum" - }, - "loan_max_renewals": { - "type": "integer", - "maximum": 100, - "minimum": 0 + "kind": { + "type": "string", + "readOnly": true }, - "loan_renewal_days": { - "type": "integer", - "maximum": 365, - "minimum": 1 + "original_filename": { + "type": "string", + "readOnly": true }, - "loan_max_active": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "content_type": { + "type": "string", + "readOnly": true }, - "loan_max_items": { + "size_bytes": { "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "readOnly": true }, - "loan_reminder_offsets_hours": { - "type": "array", - "items": { - "type": "integer", - "minimum": -8760, - "maximum": 8760 - }, - "maxItems": 100, - "uniqueItems": true, - "description": "Signed whole hours from due time: negative before, zero due, positive overdue." + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true }, - "loan_escalate_overdue_days": { + "attached_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "attached_at", + "content_type", + "created_at", + "id", + "kind", + "original_filename", + "size_bytes" + ] + }, + "ServiceFileFinalize": { + "type": "object", + "properties": { + "file_id": { "type": "integer", - "maximum": 365, - "minimum": 0 + "minimum": 1 } }, "required": [ - "id" + "file_id" ] }, - "ReturnReminderCronResponse": { + "ServiceFileFinalizeResponse": { "type": "object", "properties": { - "sent": { - "type": "integer" - }, - "skipped": { + "file_id": { "type": "integer" } }, "required": [ - "sent", - "skipped" + "file_id" ] }, - "ReturnRequest": { + "ServiceFilePresign": { "type": "object", "properties": { - "kit_checklist": { - "type": "array", - "items": { - "$ref": "#/components/schemas/KitCount" - }, - "maxItems": 3550 + "filename": { + "type": "string", + "maxLength": 255 }, - "evidence_id": { + "content_type": { + "type": "string", + "maxLength": 128 + } + }, + "required": [ + "content_type", + "filename" + ] + }, + "ServiceFilePresignResponse": { + "type": "object", + "properties": { + "file_id": { "type": "integer" }, - "box_code": { - "type": "string" + "upload": { + "$ref": "#/components/schemas/ServiceFileUpload" + } + }, + "required": [ + "file_id", + "upload" + ] + }, + "ServiceFileUpload": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" }, - "remark": { + "method": { "type": "string" }, - "resolutions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReturnItemResolution" - } + "fields": { + "type": "object", + "additionalProperties": {} + }, + "headers": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "box_code", - "evidence_id", - "remark", - "resolutions" + "url" ] }, - "ReturnsReport": { + "ServiceFileUrl": { "type": "object", "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ReturnsReportRow" - } + "url": { + "type": "string", + "format": "uri" } }, "required": [ - "rows", - "typed_rows" + "url" ] }, - "ReturnsReportRow": { + "ServiceMachine": { "type": "object", "properties": { - "makerspace_id": { - "type": "integer" - }, "id": { - "type": "integer" + "type": "integer", + "readOnly": true }, - "requester": { - "type": "string" + "name": { + "type": "string", + "readOnly": true }, "status": { - "type": "string" - }, - "closed_at": { "type": "string", - "format": "date-time", - "nullable": true + "readOnly": true } }, "required": [ - "closed_at", "id", - "requester", + "name", "status" ] }, - "RevenueOverTimeReport": { + "ServiceQueueTimesReport": { "type": "object", "properties": { "rows": { @@ -101063,7 +124170,7 @@ "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/RevenuePeriod" + "$ref": "#/components/schemas/ServiceQueueTimesRow" } } }, @@ -101072,602 +124179,633 @@ "typed_rows" ] }, - "RevenuePeriod": { + "ServiceQueueTimesRow": { "type": "object", "properties": { "makerspace_id": { "type": "integer" }, - "currency": { - "type": "string" - }, - "collected_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" - }, - "refunded_amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" - }, "period": { "type": "string", "format": "date" }, - "subject_type": { + "queue_id": { + "type": "integer", + "nullable": true + }, + "queue_name": { "type": "string" }, - "owed_amount": { + "bucket_id": { + "type": "integer", + "nullable": true + }, + "stage": { + "type": "string" + }, + "samples": { + "type": "integer" + }, + "median_minutes": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true }, - "waived_amount": { + "p90_minutes": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true }, - "net_amount": { + "p95_minutes": { "type": "string", "format": "decimal", - "pattern": "^-?\\d{0,18}(?:\\.\\d{0,2})?$" + "pattern": "^-?\\d{0,22}(?:\\.\\d{0,2})?$", + "nullable": true } }, "required": [ - "collected_amount", - "currency", - "net_amount", - "owed_amount", + "bucket_id", + "median_minutes", + "p90_minutes", + "p95_minutes", "period", - "refunded_amount", - "subject_type", - "waived_amount" + "queue_id", + "queue_name", + "samples", + "stage" ] }, - "Revoke": { + "ServiceReject": { "type": "object", "properties": { "reason": { "type": "string" } - } + }, + "required": [ + "reason" + ] }, - "Role": { + "ServiceRequester": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "makerspace_id": { - "type": "integer", - "readOnly": true - }, - "name": { + "username": { "type": "string", "readOnly": true }, - "slug": { + "display": { "type": "string", - "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "granted_actions": { - "type": "array", - "items": { - "type": "string" - }, - "readOnly": true - }, - "legacy_role": { - "readOnly": true, - "nullable": true, - "oneOf": [ - { - "$ref": "#/components/schemas/LegacyRoleEnum" - }, - { - "$ref": "#/components/schemas/NullEnum" - } - ] - }, - "is_default": { - "type": "boolean", "readOnly": true + } + }, + "required": [ + "display", + "id", + "username" + ] + }, + "ServiceStart": { + "type": "object", + "properties": { + "machine_id": { + "type": "integer", + "minimum": 1, + "nullable": true }, - "is_protected": { - "type": "boolean", - "readOnly": true + "estimated_minutes": { + "type": "integer", + "minimum": 0 }, - "member_count": { + "consumable_pool_id": { "type": "integer", - "readOnly": true + "minimum": 1 }, - "created_at": { + "planned_grams": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "updated_at": { + "planned_quantity": { "type": "string", - "format": "date-time", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" } - }, - "required": [ - "created_at", - "granted_actions", - "id", - "is_default", - "is_protected", - "legacy_role", - "makerspace_id", - "member_count", - "name", - "slug", - "updated_at" - ] - }, - "Role827Enum": { - "enum": [ - "space_manager", - "inventory_manager", - "print_manager", - "machine_manager", - "custom" - ], - "type": "string", - "description": "* `space_manager` - Space Manager\n* `inventory_manager` - Inventory Manager\n* `print_manager` - Print Manager\n* `machine_manager` - Machine Manager\n* `custom` - Custom" + } }, - "RoleCreate": { + "SetStatus": { "type": "object", "properties": { - "name": { + "status": { "type": "string", - "maxLength": 80 - }, - "granted_actions": { - "type": "array", - "items": { - "type": "string" - } + "maxLength": 20 } }, "required": [ - "granted_actions", - "name" + "status" ] }, - "RoleId": { + "SetupState": { "type": "object", "properties": { - "role_id": { - "type": "integer" + "profile_set": { + "type": "boolean" + }, + "hours_set": { + "type": "boolean" + }, + "first_item_added": { + "type": "boolean" + }, + "staff_invited": { + "type": "boolean" + }, + "modules_chosen": { + "type": "boolean" } }, "required": [ - "role_id" + "first_item_added", + "hours_set", + "modules_chosen", + "profile_set", + "staff_invited" ] }, - "RoleMachineScope": { + "SocialIdentity": { "type": "object", - "description": "The role's links plus everything it could be linked to, in one round trip.", "properties": { - "machine_type_ids": { - "type": "array", - "items": { - "type": "integer" - } - }, - "machine_ids": { - "type": "array", - "items": { - "type": "integer" - } - }, - "available_machine_types": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineScopeOption" - }, - "readOnly": true + "provider": { + "type": "string" }, - "available_machines": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MachineScopeOption" - }, - "readOnly": true + "created_at": { + "type": "string", + "format": "date-time" }, - "scoping_applies": { - "type": "boolean", - "readOnly": true + "updated_at": { + "type": "string", + "format": "date-time" } }, "required": [ - "available_machine_types", - "available_machines", - "machine_ids", - "machine_type_ids", - "scoping_applies" + "created_at", + "provider", + "updated_at" ] }, - "RoleMachineScopeWrite": { + "SocialLink": { "type": "object", "properties": { - "machine_type_ids": { - "type": "array", - "items": { - "type": "integer" - } + "provider": { + "type": "string", + "maxLength": 64 }, - "machine_ids": { - "type": "array", - "items": { - "type": "integer" - } + "id_token": { + "type": "string", + "writeOnly": true, + "maxLength": 16384 + }, + "nonce": { + "type": "string", + "writeOnly": true, + "maxLength": 512 + }, + "client_platform": { + "allOf": [ + { + "$ref": "#/components/schemas/ClientPlatformEnum" + } + ], + "default": "web" + }, + "apple_name": { + "type": "string", + "maxLength": 200 } }, "required": [ - "machine_ids", - "machine_type_ids" + "id_token", + "nonce", + "provider" ] }, - "RoleSlugEnum": { - "enum": [ - "space_manager", - "inventory_manager", - "machine_manager" - ], - "type": "string", - "description": "* `space_manager` - space_manager\n* `inventory_manager` - inventory_manager\n* `machine_manager` - machine_manager" - }, - "SavedReportView": { + "SocialLogin": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "authorization_code": { + "type": "string", + "writeOnly": true, + "description": "Optional Apple authorization code for later token revocation. Exchange failures never fail sign-in.", + "maxLength": 4096 }, - "makerspace": { - "type": "integer", - "readOnly": true + "redirect_uri": { + "description": "Original same-origin Apple web authorization redirect URI; required when exchanging a web code.", + "oneOf": [ + { + "type": "string", + "format": "uri", + "default": "", + "maxLength": 2048 + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "owner": { - "type": "integer", - "readOnly": true + "id_token": { + "type": "string", + "writeOnly": true, + "maxLength": 16384 }, - "name": { + "nonce": { "type": "string", - "maxLength": 80 + "writeOnly": true, + "maxLength": 512 }, - "state": { - "$ref": "#/components/schemas/ReportViewState" + "surface": { + "$ref": "#/components/schemas/Surface356Enum" }, - "shared": { - "type": "boolean" + "delivery": { + "$ref": "#/components/schemas/DeliveryEnum" }, - "created_at": { + "client_platform": { + "$ref": "#/components/schemas/ClientPlatformEnum" + }, + "apple_name": { "type": "string", - "format": "date-time", - "readOnly": true + "maxLength": 200 }, - "updated_at": { + "challenge": { "type": "string", - "format": "date-time", - "readOnly": true + "writeOnly": true, + "maxLength": 512 + }, + "attestation": { + "allOf": [ + { + "$ref": "#/components/schemas/DeviceAttestationPayload" + } + ], + "writeOnly": true } }, "required": [ - "created_at", - "id", - "makerspace", - "name", - "owner", - "state", - "updated_at" + "client_platform", + "delivery", + "id_token", + "nonce", + "surface" ] }, - "SavedReportViewList": { + "SocialLoginResponse": { "type": "object", "properties": { - "count": { - "type": "integer" + "access": { + "type": "string" }, - "next": { - "type": "string", - "format": "uri", - "nullable": true + "refresh": { + "type": "string" }, - "previous": { - "type": "string", - "format": "uri", - "nullable": true + "device_grant": { + "type": "object", + "additionalProperties": {} }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SavedReportView" - } + "user": { + "type": "object", + "additionalProperties": {} + }, + "outcome": { + "type": "string" } }, "required": [ - "count", - "next", - "previous", - "results" + "access", + "outcome", + "user" ] }, - "SavedView": { + "SocialNonce": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer", - "readOnly": true - }, - "owner": { - "type": "integer", - "readOnly": true - }, - "owner_username": { - "type": "string", - "readOnly": true - }, - "name": { + "provider": { "type": "string", - "maxLength": 80 + "maxLength": 64 }, - "entity": { - "$ref": "#/components/schemas/SavedViewEntityEnum" + "surface": { + "$ref": "#/components/schemas/Surface356Enum" }, - "state": { - "type": "object", - "additionalProperties": { - "type": "string", - "maxLength": 250 - } + "delivery": { + "$ref": "#/components/schemas/DeliveryEnum" }, - "shared": { - "type": "boolean" + "client_platform": { + "$ref": "#/components/schemas/ClientPlatformEnum" }, - "created_at": { + "challenge": { "type": "string", - "format": "date-time", - "readOnly": true + "writeOnly": true, + "maxLength": 512 + } + }, + "required": [ + "client_platform", + "delivery", + "provider", + "surface" + ] + }, + "SocialNonceResponse": { + "type": "object", + "properties": { + "nonce": { + "type": "string" }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "expires_in": { + "type": "integer" } }, "required": [ - "created_at", - "entity", - "id", - "makerspace", - "name", - "owner", - "owner_username", - "state", - "updated_at" + "expires_in", + "nonce" ] }, - "SavedViewEntityEnum": { - "enum": [ - "requests", - "inventory", - "members", - "payments", - "bookings", - "events", - "audit" - ], - "type": "string", - "description": "* `requests` - requests\n* `inventory` - inventory\n* `members` - members\n* `payments` - payments\n* `bookings` - bookings\n* `events` - events\n* `audit` - audit" + "SpaceImageFinalizeRequest": { + "type": "object", + "properties": { + "object_key": { + "type": "string", + "maxLength": 500 + } + }, + "required": [ + "object_key" + ] }, - "SavedViewList": { + "SpaceImagePresignRequest": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { + "filename": { "type": "string", - "format": "uri", - "nullable": true + "maxLength": 255 }, - "previous": { + "content_type": { "type": "string", - "format": "uri", - "nullable": true - }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SavedView" - } + "maxLength": 100 } }, "required": [ - "count", - "next", - "previous", - "results" + "content_type", + "filename" ] }, - "Schedule": { + "SpaceImagePresignResponse": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "object_key": { + "type": "string" }, - "name": { + "upload": { + "$ref": "#/components/schemas/SpaceImageUpload" + } + }, + "required": [ + "object_key", + "upload" + ] + }, + "SpaceImageUpload": { + "type": "object", + "properties": { + "url": { "type": "string", - "maxLength": 200 - }, - "container": { - "type": "integer", - "nullable": true - }, - "interval_days": { - "type": "integer", - "minimum": 1 + "format": "uri" }, - "next_due": { - "type": "string", - "format": "date" + "method": { + "$ref": "#/components/schemas/SpaceImageUploadMethodEnum" }, - "blind": { - "type": "boolean" + "fields": { + "type": "object", + "additionalProperties": {} }, - "is_active": { - "type": "boolean" + "headers": { + "type": "object", + "additionalProperties": {} } }, "required": [ - "id", - "interval_days", - "name", - "next_due" + "url" ] }, - "ScopeEnum": { - "enum": [ - "deployment", - "makerspace" - ], - "type": "string", - "description": "* `deployment` - Full deployment\n* `makerspace` - Makerspace" - }, - "ScopeModeEnum": { + "SpaceImageUploadMethodEnum": { "enum": [ - "machine", - "full" + "PUT" ], "type": "string", - "description": "* `machine` - machine\n* `full` - full" + "description": "* `PUT` - PUT" }, - "SearchHit": { + "SpacePack": { "type": "object", "properties": { - "entity": { - "$ref": "#/components/schemas/SearchHitEntityEnum" - }, - "id": { - "type": "integer" + "format": { + "$ref": "#/components/schemas/SpacePackFormatEnum" }, - "label": { - "type": "string" + "version": { + "type": "integer", + "maximum": 1, + "minimum": 1 }, - "tab": { - "type": "string" + "configuration": { + "allOf": [ + { + "$ref": "#/components/schemas/PackConfiguration" + } + ], + "description": "Configuration-only sections; see docs/SPACE-PACKS.md." }, - "query": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "checksum": { + "type": "string", + "maxLength": 64, + "minLength": 64 } }, "required": [ - "entity", - "id", - "label", - "query", - "tab" + "checksum", + "configuration", + "format", + "version" ] }, - "SearchHitEntityEnum": { + "SpacePackFormatEnum": { "enum": [ - "requests", - "inventory", - "members", - "bookings", - "events", - "machines" + "spaceworks-space-pack" ], "type": "string", - "description": "* `requests` - requests\n* `inventory` - inventory\n* `members` - members\n* `bookings` - bookings\n* `events` - events\n* `machines` - machines" + "description": "* `spaceworks-space-pack` - spaceworks-space-pack" }, - "SearchOutput": { + "SponsorTierEnum": { + "enum": [ + "platinum", + "gold", + "silver", + "partner", + "community_partner" + ], + "type": "string", + "description": "* `platinum` - platinum\n* `gold` - gold\n* `silver` - silver\n* `partner` - partner\n* `community_partner` - community_partner" + }, + "StaffBookingInput": { "type": "object", "properties": { - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearchHit" - } + "certification_override_reason": { + "type": "string", + "description": "Requires existing authority over the linked machine type; audited.", + "maxLength": 1000 + }, + "starts_at": { + "type": "string", + "format": "date-time", + "writeOnly": true + }, + "ends_at": { + "type": "string", + "format": "date-time", + "writeOnly": true + }, + "custom_answers": { + "writeOnly": true, + "nullable": true + }, + "member_id": { + "type": "integer", + "maximum": 9223372036854775807, + "minimum": 1, + "format": "int64" + }, + "note": { + "type": "string", + "maxLength": 5000 } }, "required": [ - "results" + "ends_at", + "member_id", + "starts_at" ] }, - "SegmentChoice": { + "StaffGrant": { "type": "object", "properties": { "id": { - "type": "integer" + "type": "integer", + "readOnly": true }, - "name": { - "type": "string" + "makerspace": { + "type": "integer", + "readOnly": true + }, + "event": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "series": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "user": { + "type": "integer", + "readOnly": true + }, + "permissions": { + "readOnly": true + }, + "granted_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_by": { + "type": "integer", + "readOnly": true, + "nullable": true } }, "required": [ + "created_at", + "event", + "granted_by", "id", - "name" + "makerspace", + "permissions", + "revoked_at", + "revoked_by", + "series", + "user" ] }, - "SeriesCollaborationInbox": { + "StaffGrantInvite": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "series_id": { + "makerspace": { + "type": "integer", + "readOnly": true + }, + "event": { "type": "integer", - "readOnly": true + "readOnly": true, + "nullable": true }, - "series_title": { - "type": "string", - "readOnly": true + "series": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "host_name": { - "type": "string", + "permissions": { "readOnly": true }, - "host_slug": { - "type": "string", + "created_by": { + "type": "integer", "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" + "nullable": true }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusB9dEnum" - } - ], + "created_at": { + "type": "string", + "format": "date-time", "readOnly": true }, - "next_occurrence_at": { + "expires_at": { "type": "string", "format": "date-time", "readOnly": true }, - "created_at": { + "redeemed_at": { "type": "string", "format": "date-time", - "readOnly": true + "readOnly": true, + "nullable": true }, - "responded_at": { + "revoked_at": { "type": "string", "format": "date-time", "readOnly": true, @@ -101676,65 +124814,42 @@ }, "required": [ "created_at", - "host_name", - "host_slug", + "created_by", + "event", + "expires_at", "id", - "next_occurrence_at", - "responded_at", - "series_id", - "series_title", - "status" - ] - }, - "SeriesCollaborationRespond": { - "type": "object", - "properties": { - "accept": { - "type": "boolean" - } - }, - "required": [ - "accept" + "makerspace", + "permissions", + "redeemed_at", + "revoked_at", + "series" ] }, - "SeriesCollaborator": { + "StaffGrantInviteCreated": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "series_id": { + "makerspace": { "type": "integer", "readOnly": true }, - "makerspace_id": { + "event": { "type": "integer", - "readOnly": true - }, - "makerspace_name": { - "type": "string", - "readOnly": true - }, - "makerspace_slug": { - "type": "string", "readOnly": true, - "pattern": "^[-a-zA-Z0-9_]+$" - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusB9dEnum" - } - ], - "readOnly": true + "nullable": true }, - "invited_by_id": { + "series": { "type": "integer", "readOnly": true, "nullable": true }, - "responded_by_id": { + "permissions": { + "readOnly": true + }, + "created_by": { "type": "integer", "readOnly": true, "nullable": true @@ -101744,119 +124859,183 @@ "format": "date-time", "readOnly": true }, - "responded_at": { + "expires_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "redeemed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "revoked_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true + }, + "token": { + "type": "string", + "readOnly": true } }, "required": [ "created_at", + "created_by", + "event", + "expires_at", "id", - "invited_by_id", - "makerspace_id", - "makerspace_name", - "makerspace_slug", - "responded_at", - "responded_by_id", - "series_id", - "status" + "makerspace", + "permissions", + "redeemed_at", + "revoked_at", + "series", + "token" ] }, - "SeriesCollaboratorReplace": { + "StaffGrantInviteInput": { "type": "object", "properties": { - "slugs": { + "permissions": { "type": "array", "items": { - "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" - } + "$ref": "#/components/schemas/Permissions5d1Enum" + }, + "maxItems": 5, + "minItems": 1 + }, + "email": { + "oneOf": [ + { + "type": "string", + "format": "email", + "default": "" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "send_email": { + "type": "boolean", + "default": false } }, "required": [ - "slugs" + "permissions" ] }, - "ServiceAccept": { + "StaffGrantInviteList": { "type": "object", "properties": { - "estimated_minutes": { - "type": "integer", - "minimum": 0 + "count": { + "type": "integer" }, - "planned_grams": { + "next": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "uri", + "nullable": true }, - "note": { - "type": "string" + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StaffGrantInvite" + } } - } + }, + "required": [ + "count", + "next", + "previous", + "results" + ] }, - "ServiceComplete": { + "StaffGrantList": { "type": "object", "properties": { - "actual_minutes": { - "type": "integer", - "minimum": 0 + "count": { + "type": "integer" }, - "actual_grams": { + "next": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "uri", + "nullable": true }, - "actual_quantity": { + "previous": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "format": "uri", + "nullable": true }, - "consumptions": { + "results": { "type": "array", "items": { - "$ref": "#/components/schemas/ServiceConsumptionInput" + "$ref": "#/components/schemas/StaffGrant" } } }, "required": [ - "actual_minutes" + "count", + "next", + "previous", + "results" ] }, - "ServiceConsumption": { + "StaffGrantRedeem": { + "type": "object", + "properties": { + "token": { + "type": "string", + "writeOnly": true, + "maxLength": 128, + "minLength": 32 + } + }, + "required": [ + "token" + ] + }, + "StaffInvitation": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "machine_consumable_id": { - "type": "integer", - "readOnly": true - }, - "measurement": { + "email": { "type": "string", "readOnly": true }, - "product_id": { - "type": "integer", - "readOnly": true, - "nullable": true + "makerspace": { + "type": "integer" }, - "label": { + "role": { + "type": "integer" + }, + "role_name": { "type": "string", "readOnly": true }, - "quantity": { + "expires_at": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true + "format": "date-time" }, - "outcome": { + "accepted_at": { "type": "string", - "readOnly": true + "format": "date-time", + "nullable": true + }, + "revoked_at": { + "type": "string", + "format": "date-time", + "nullable": true }, "created_at": { "type": "string", @@ -101866,915 +125045,1125 @@ }, "required": [ "created_at", + "email", + "expires_at", "id", - "label", - "machine_consumable_id", - "measurement", - "outcome", - "product_id", - "quantity" + "makerspace", + "role", + "role_name" ] }, - "ServiceConsumptionInput": { + "StaffInvitationAccept": { "type": "object", "properties": { - "machine_consumable_id": { - "type": "integer", - "minimum": 1 + "token": { + "type": "string", + "writeOnly": true, + "maxLength": 200, + "minLength": 20 }, - "quantity": { + "makerspace_slug": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "password": { + "type": "string", + "writeOnly": true, + "maxLength": 1024 + }, + "code": { + "type": "string", + "writeOnly": true, + "default": "", + "maxLength": 64 } }, "required": [ - "machine_consumable_id", - "quantity" + "makerspace_slug", + "password", + "token" ] }, - "ServiceFail": { + "StaffInvitationAccepted": { "type": "object", "properties": { - "actual_minutes": { - "type": "integer", - "minimum": 0 - }, - "actual_grams": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "actual_quantity": { + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "StaffInvitationCreate": { + "type": "object", + "properties": { + "email": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "consumptions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ServiceConsumptionInput" - } + "format": "email", + "maxLength": 254 }, - "reason": { - "type": "string" + "role_id": { + "type": "integer", + "minimum": 1 }, - "percent_complete": { + "expires_in_days": { "type": "integer", - "maximum": 100, - "minimum": 0 + "maximum": 30, + "minimum": 1, + "default": 7 } }, "required": [ - "actual_minutes", - "percent_complete", - "reason" + "email", + "role_id" ] }, - "ServiceFile": { + "StaffInvitationDelivery": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "kind": { + "email": { "type": "string", "readOnly": true }, - "original_filename": { + "makerspace": { + "type": "integer" + }, + "role": { + "type": "integer" + }, + "role_name": { "type": "string", "readOnly": true }, - "content_type": { + "expires_at": { "type": "string", - "readOnly": true + "format": "date-time" }, - "size_bytes": { - "type": "integer", - "readOnly": true + "accepted_at": { + "type": "string", + "format": "date-time", + "nullable": true }, - "created_at": { + "revoked_at": { "type": "string", "format": "date-time", - "readOnly": true + "nullable": true }, - "attached_at": { + "created_at": { "type": "string", "format": "date-time", "readOnly": true + }, + "accept_url": { + "type": "string" + }, + "delivery_status": { + "type": "string" } }, "required": [ - "attached_at", - "content_type", + "accept_url", "created_at", + "delivery_status", + "email", + "expires_at", "id", - "kind", - "original_filename", - "size_bytes" + "makerspace", + "role", + "role_name" ] }, - "ServiceFileFinalize": { + "StaffMembership": { "type": "object", "properties": { - "file_id": { + "id": { "type": "integer", - "minimum": 1 - } - }, - "required": [ - "file_id" - ] - }, - "ServiceFileFinalizeResponse": { - "type": "object", - "properties": { - "file_id": { + "readOnly": true + }, + "user": { + "$ref": "#/components/schemas/User" + }, + "makerspace_id": { "type": "integer" - } - }, - "required": [ - "file_id" - ] - }, - "ServiceFilePresign": { - "type": "object", - "properties": { - "filename": { + }, + "makerspace_slug": { "type": "string", - "maxLength": 255 + "pattern": "^[-a-zA-Z0-9_]+$" }, - "content_type": { + "role": { + "$ref": "#/components/schemas/RoleCddEnum" + }, + "created_at": { "type": "string", - "maxLength": 128 + "format": "date-time", + "readOnly": true } }, "required": [ - "content_type", - "filename" + "created_at", + "id", + "makerspace_id", + "makerspace_slug", + "user" ] }, - "ServiceFilePresignResponse": { + "StaffPayment": { "type": "object", "properties": { - "file_id": { - "type": "integer" + "id": { + "type": "integer", + "readOnly": true }, - "upload": { - "$ref": "#/components/schemas/ServiceFileUpload" - } - }, - "required": [ - "file_id", - "upload" - ] - }, - "ServiceFileUpload": { - "type": "object", - "properties": { - "url": { + "subject_type": { + "$ref": "#/components/schemas/SubjectTypeEnum" + }, + "subject_label": { "type": "string", - "format": "uri" + "readOnly": true }, - "method": { - "type": "string" + "status": { + "$ref": "#/components/schemas/Status66aEnum" }, - "fields": { - "type": "object", - "additionalProperties": {} + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "headers": { - "type": "object", - "additionalProperties": {} - } - }, - "required": [ - "url" - ] - }, - "ServiceFileUrl": { - "type": "object", - "properties": { - "url": { + "currency": { + "type": "string", + "pattern": "^[a-z]{3}$", + "maxLength": 3 + }, + "checkout_url": { + "type": "string", + "readOnly": true + }, + "online_payment_available": { + "type": "boolean", + "description": "Whether offering to pay online is honest for this charge.\n\nResolved once per makerspace by the view and passed in: doing it per row would\nre-read settings and credentials for every charge in the list. Falls back to a\nlive check only when a caller has not supplied it.", + "readOnly": true + }, + "settlement": { + "allOf": [ + { + "$ref": "#/components/schemas/MemberSettlement" + } + ], + "nullable": true, + "readOnly": true + }, + "created_at": { "type": "string", - "format": "uri" + "format": "date-time", + "readOnly": true } }, "required": [ - "url" + "amount", + "checkout_url", + "created_at", + "currency", + "id", + "online_payment_available", + "settlement", + "subject_label", + "subject_type" ] }, - "ServiceMachine": { + "StaffPaymentSummary": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "name": { + "status": { + "allOf": [ + { + "$ref": "#/components/schemas/Status66aEnum" + } + ], + "readOnly": true + }, + "amount": { "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "status": { + "currency": { "type": "string", "readOnly": true } }, "required": [ + "amount", + "currency", "id", - "name", "status" ] }, - "ServiceReject": { - "type": "object", - "properties": { - "reason": { - "type": "string" - } - }, - "required": [ - "reason" - ] - }, - "ServiceRequester": { + "StaffSettlement": { "type": "object", + "description": "The receipt that currently stands for a charge settled at the desk.\n\n`corrected` says an earlier receipt was replaced through amend-settlement; the chain\nitself stays in the ledger and the audit log.", "properties": { "id": { "type": "integer", "readOnly": true }, - "username": { + "method": { + "allOf": [ + { + "$ref": "#/components/schemas/Method2edEnum" + } + ], + "readOnly": true + }, + "reference": { "type": "string", "readOnly": true }, - "display": { + "received_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "recorded_at": { "type": "string", + "format": "date-time", + "readOnly": true + }, + "corrected": { + "type": "boolean", "readOnly": true } }, "required": [ - "display", + "corrected", "id", - "username" + "method", + "received_at", + "recorded_at", + "reference" ] }, - "ServiceStart": { - "type": "object", - "properties": { - "machine_id": { - "type": "integer", - "minimum": 1, - "nullable": true - }, - "estimated_minutes": { - "type": "integer", - "minimum": 0 - }, - "consumable_pool_id": { - "type": "integer", - "minimum": 1 - }, - "planned_grams": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "planned_quantity": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - } - } + "StageEnum": { + "enum": [ + "requested", + "claimed", + "preflight", + "quiesced", + "db_restoring", + "objects_restoring", + "validating", + "completed", + "restored_quarantined", + "rolling_back", + "failed", + "aborted" + ], + "type": "string", + "description": "* `requested` - Requested\n* `claimed` - Claimed\n* `preflight` - Preflight\n* `quiesced` - Quiesced\n* `db_restoring` - Database restoring\n* `objects_restoring` - Objects restoring\n* `validating` - Validating\n* `completed` - Completed\n* `restored_quarantined` - Restored quarantined\n* `rolling_back` - Rolling back\n* `failed` - Failed\n* `aborted` - Aborted" }, - "SetStatus": { + "State31eEnum": { + "enum": [ + "active", + "expired", + "revoked", + "redeemed" + ], + "type": "string", + "description": "* `active` - active\n* `expired` - expired\n* `revoked` - revoked\n* `redeemed` - redeemed" + }, + "State9efEnum": { + "enum": [ + "requested", + "invited", + "awaiting_vouches", + "awaiting_payment", + "expired", + "canceled", + "active", + "revoked" + ], + "type": "string", + "description": "* `requested` - Requested\n* `invited` - Invited\n* `awaiting_vouches` - Awaiting vouches\n* `awaiting_payment` - Awaiting payment\n* `expired` - Expired\n* `canceled` - Canceled\n* `active` - Active\n* `revoked` - Revoked" + }, + "StationPin": { "type": "object", "properties": { - "status": { + "pin": { "type": "string", - "maxLength": 20 + "writeOnly": true, + "pattern": "^\\d{8}$" } }, "required": [ - "status" + "pin" ] }, - "SetupState": { + "StationReveal": { "type": "object", "properties": { - "profile_set": { - "type": "boolean" - }, - "hours_set": { - "type": "boolean" - }, - "first_item_added": { - "type": "boolean" - }, - "staff_invited": { - "type": "boolean" - }, - "modules_chosen": { - "type": "boolean" + "current_password": { + "type": "string", + "writeOnly": true } }, "required": [ - "first_item_added", - "hours_set", - "modules_chosen", - "profile_set", - "staff_invited" + "current_password" ] }, - "SeverityEnum": { - "enum": [ - "info", - "warning", - "error", - "critical" - ], - "type": "string", - "description": "* `info` - Info\n* `warning` - Warning\n* `error` - Error\n* `critical` - Critical" - }, - "SocialIdentity": { + "StationRevealResponse": { "type": "object", "properties": { - "provider": { + "pin": { "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" + "version": { + "type": "integer" } }, "required": [ - "created_at", - "provider", - "updated_at" + "pin", + "version" ] }, - "SocialLink": { + "StationRotation": { "type": "object", "properties": { - "provider": { - "type": "string", - "maxLength": 64 - }, - "id_token": { - "type": "string", - "writeOnly": true, - "maxLength": 16384 + "pin": { + "type": "string" }, - "nonce": { + "public_token": { "type": "string", - "writeOnly": true, - "maxLength": 512 + "format": "uuid" }, - "client_platform": { - "allOf": [ - { - "$ref": "#/components/schemas/ClientPlatformEnum" - } - ], - "default": "web" + "version": { + "type": "integer" }, - "apple_name": { + "station_url": { "type": "string", - "maxLength": 200 + "format": "uri" } }, "required": [ - "id_token", - "nonce", - "provider" + "pin", + "public_token", + "station_url", + "version" ] }, - "SocialLogin": { + "StationStatus": { "type": "object", "properties": { - "id_token": { - "type": "string", - "writeOnly": true, - "maxLength": 16384 - }, - "nonce": { - "type": "string", - "writeOnly": true, - "maxLength": 512 + "configured": { + "type": "boolean" }, - "surface": { - "$ref": "#/components/schemas/Surface356Enum" + "enabled": { + "type": "boolean" }, - "delivery": { - "$ref": "#/components/schemas/DeliveryEnum" + "public_token": { + "type": "string", + "format": "uuid" }, - "client_platform": { - "$ref": "#/components/schemas/ClientPlatformEnum" + "version": { + "type": "integer" }, - "apple_name": { + "station_url": { "type": "string", - "maxLength": 200 + "format": "uri" }, - "challenge": { + "rotated_at": { "type": "string", - "writeOnly": true, - "maxLength": 512 - }, - "attestation": { - "writeOnly": true + "format": "date-time" } }, "required": [ - "client_platform", - "delivery", - "id_token", - "nonce", - "surface" + "configured" ] }, - "SocialLoginResponse": { + "Status00eEnum": { + "enum": [ + "ok", + "warn", + "error" + ], + "type": "string", + "description": "* `ok` - ok\n* `warn` - warn\n* `error` - error" + }, + "Status2caEnum": { + "enum": [ + "pending", + "confirmed", + "rejected", + "cancelled", + "completed", + "no_show" + ], + "type": "string", + "description": "* `pending` - Pending\n* `confirmed` - Confirmed\n* `rejected` - Rejected\n* `cancelled` - Cancelled\n* `completed` - Completed\n* `no_show` - No-show" + }, + "Status37fEnum": { + "enum": [ + "active", + "revoked" + ], + "type": "string", + "description": "* `active` - Active\n* `revoked` - Revoked" + }, + "Status66aEnum": { + "enum": [ + "pending", + "paid_online", + "paid_offline", + "waived", + "canceled" + ], + "type": "string", + "description": "* `pending` - Pending\n* `paid_online` - Paid online\n* `paid_offline` - Paid offline\n* `waived` - Waived\n* `canceled` - Canceled" + }, + "Status83eEnum": { + "enum": [ + "ok", + "warn", + "error", + "unknown" + ], + "type": "string", + "description": "* `ok` - ok\n* `warn` - warn\n* `error` - error\n* `unknown` - unknown" + }, + "StatusA39Enum": { + "enum": [ + "pending", + "verified", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `verified` - Verified\n* `failed` - Failed" + }, + "StatusAdeEnum": { + "enum": [ + "draft", + "queued", + "sending", + "sent", + "cancelled", + "failed" + ], + "type": "string", + "description": "* `draft` - Draft\n* `queued` - Queued\n* `sending` - Sending\n* `sent` - Sent\n* `cancelled` - Cancelled\n* `failed` - Failed" + }, + "StatusB9dEnum": { + "enum": [ + "invited", + "accepted", + "declined" + ], + "type": "string", + "description": "* `invited` - Invited\n* `accepted` - Accepted\n* `declined` - Declined" + }, + "StatusDf6Enum": { + "enum": [ + "pending_approval", + "registered", + "waitlisted", + "rejected", + "cancelled", + "attended" + ], + "type": "string", + "description": "* `pending_approval` - Pending approval\n* `registered` - Registered\n* `waitlisted` - Waitlisted\n* `rejected` - Rejected\n* `cancelled` - Cancelled\n* `attended` - Attended" + }, + "StatusE1dEnum": { + "enum": [ + "pending", + "running", + "available", + "failed" + ], + "type": "string", + "description": "* `pending` - Pending\n* `running` - Running\n* `available` - Available\n* `failed` - Failed" + }, + "StatusE75Enum": { + "enum": [ + "active", + "suspended" + ], + "type": "string", + "description": "* `active` - Active\n* `suspended` - Suspended" + }, + "StatusE90Enum": { + "enum": [ + "published" + ], + "type": "string", + "description": "* `published` - published" + }, + "StatusE94Enum": { + "enum": [ + "pending", + "approved", + "rejected" + ], + "type": "string", + "description": "* `pending` - Pending\n* `approved` - Approved\n* `rejected` - Rejected" + }, + "StatusFbbEnum": { + "enum": [ + "draft", + "published", + "cancelled", + "completed" + ], + "type": "string", + "description": "* `draft` - Draft\n* `published` - Published\n* `cancelled` - Cancelled\n* `completed` - Completed" + }, + "Statuses219Enum": { + "enum": [ + "pending", + "confirmed" + ], + "type": "string", + "description": "* `pending` - pending\n* `confirmed` - confirmed" + }, + "StatusesDf6Enum": { + "enum": [ + "pending_approval", + "registered", + "waitlisted", + "rejected", + "cancelled", + "attended" + ], + "type": "string", + "description": "* `pending_approval` - Pending approval\n* `registered` - Registered\n* `waitlisted` - Waitlisted\n* `rejected` - Rejected\n* `cancelled` - Cancelled\n* `attended` - Attended" + }, + "StockTransfer": { "type": "object", "properties": { - "access": { - "type": "string" - }, - "refresh": { - "type": "string" + "id": { + "type": "integer", + "readOnly": true }, - "device_grant": { - "type": "object", - "additionalProperties": {} + "makerspace": { + "type": "integer" }, - "user": { - "type": "object", - "additionalProperties": {} + "source_container": { + "type": "integer", + "nullable": true }, - "outcome": { - "type": "string" - } - }, - "required": [ - "access", - "outcome", - "user" - ] - }, - "SocialNonce": { - "type": "object", - "properties": { - "provider": { - "type": "string", - "maxLength": 64 + "destination_container": { + "type": "integer", + "nullable": true }, - "surface": { - "$ref": "#/components/schemas/Surface356Enum" + "source_makerspace": { + "type": "integer", + "nullable": true }, - "delivery": { - "$ref": "#/components/schemas/DeliveryEnum" + "destination_makerspace": { + "type": "integer", + "nullable": true }, - "client_platform": { - "$ref": "#/components/schemas/ClientPlatformEnum" + "created_by": { + "type": "integer", + "nullable": true }, - "challenge": { - "type": "string", - "writeOnly": true, - "maxLength": 512 - } - }, - "required": [ - "client_platform", - "delivery", - "provider", - "surface" - ] - }, - "SocialNonceResponse": { - "type": "object", - "properties": { - "nonce": { + "reason": { "type": "string" }, - "expires_in": { - "type": "integer" - } - }, - "required": [ - "expires_in", - "nonce" - ] - }, - "SpaceImageFinalizeRequest": { - "type": "object", - "properties": { - "object_key": { - "type": "string", - "maxLength": 500 - } - }, - "required": [ - "object_key" - ] - }, - "SpaceImagePresignRequest": { - "type": "object", - "properties": { - "filename": { + "status": { + "$ref": "#/components/schemas/StockTransferStatusEnum" + }, + "created_at": { "type": "string", - "maxLength": 255 + "format": "date-time", + "readOnly": true }, - "content_type": { + "applied_at": { "type": "string", - "maxLength": 100 + "format": "date-time", + "nullable": true + }, + "lines": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StockTransferLine" + }, + "readOnly": true } }, "required": [ - "content_type", - "filename" + "created_at", + "id", + "lines", + "makerspace", + "reason" ] }, - "SpaceImagePresignResponse": { + "StockTransferCreate": { "type": "object", "properties": { - "object_key": { + "source_container_id": { + "type": "integer", + "nullable": true + }, + "destination_container_id": { + "type": "integer", + "nullable": true + }, + "destination_makerspace_id": { + "type": "integer", + "nullable": true + }, + "reason": { "type": "string" }, - "upload": { - "$ref": "#/components/schemas/SpaceImageUpload" + "lines": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StockTransferLineInput" + } } }, "required": [ - "object_key", - "upload" + "lines", + "reason" ] }, - "SpaceImageUpload": { + "StockTransferLine": { "type": "object", "properties": { - "url": { - "type": "string", - "format": "uri" + "id": { + "type": "integer", + "readOnly": true }, - "method": { - "$ref": "#/components/schemas/SpaceImageUploadMethodEnum" + "product": { + "type": "integer", + "nullable": true }, - "fields": { - "type": "object", - "additionalProperties": {} + "asset": { + "type": "integer", + "nullable": true }, - "headers": { - "type": "object", - "additionalProperties": {} + "quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "from_status": { + "type": "string", + "maxLength": 32 + }, + "to_status": { + "type": "string", + "maxLength": 32 + }, + "notes": { + "type": "string" } }, "required": [ - "url" + "id" ] }, - "SpaceImageUploadMethodEnum": { - "enum": [ - "PUT" - ], - "type": "string", - "description": "* `PUT` - PUT" - }, - "SpacePack": { + "StockTransferLineInput": { "type": "object", "properties": { - "format": { - "$ref": "#/components/schemas/SpacePackFormatEnum" + "product_id": { + "type": "integer" }, - "version": { + "asset_id": { + "type": "integer" + }, + "quantity": { "type": "integer", - "maximum": 1, - "minimum": 1 + "minimum": 1, + "default": 1 }, - "configuration": { - "allOf": [ + "from_status": { + "oneOf": [ { - "$ref": "#/components/schemas/PackConfiguration" + "$ref": "#/components/schemas/InventoryAssetStatusEnum" + }, + { + "$ref": "#/components/schemas/BlankEnum" } - ], - "description": "Configuration-only sections; see docs/SPACE-PACKS.md." + ] }, - "checksum": { - "type": "string", - "maxLength": 64, - "minLength": 64 + "to_status": { + "oneOf": [ + { + "$ref": "#/components/schemas/InventoryAssetStatusEnum" + }, + { + "$ref": "#/components/schemas/BlankEnum" + } + ] + }, + "notes": { + "type": "string" } - }, - "required": [ - "checksum", - "configuration", - "format", - "version" - ] + } }, - "SpacePackFormatEnum": { + "StockTransferStatusEnum": { "enum": [ - "spaceworks-space-pack" + "applied", + "cancelled" ], "type": "string", - "description": "* `spaceworks-space-pack` - spaceworks-space-pack" + "description": "* `applied` - Applied\n* `cancelled` - Cancelled" }, - "StaffBookingInput": { + "Stocktake": { "type": "object", "properties": { - "certification_override_reason": { - "type": "string", - "description": "Requires existing authority over the linked machine type; audited.", - "maxLength": 1000 + "id": { + "type": "integer", + "readOnly": true }, - "starts_at": { + "makerspace": { + "type": "integer" + }, + "container": { + "type": "integer", + "nullable": true + }, + "status": { + "$ref": "#/components/schemas/StocktakeStatusEnum" + }, + "started_by": { + "type": "integer", + "nullable": true + }, + "approved_by": { + "type": "integer", + "nullable": true + }, + "started_at": { "type": "string", "format": "date-time", - "writeOnly": true + "readOnly": true }, - "ends_at": { + "completed_at": { "type": "string", "format": "date-time", - "writeOnly": true + "nullable": true }, - "custom_answers": { - "writeOnly": true, + "approved_at": { + "type": "string", + "format": "date-time", "nullable": true }, - "member_id": { + "notes": { + "type": "string" + }, + "blind": { + "type": "boolean" + }, + "variance_threshold": { "type": "integer", - "maximum": 9223372036854775807, - "minimum": 1, - "format": "int64" + "maximum": 2147483647, + "minimum": 0, + "nullable": true }, - "note": { - "type": "string", - "maxLength": 5000 + "approval_required": { + "type": "boolean", + "readOnly": true + }, + "lines": { + "type": "array", + "items": { + "$ref": "#/components/schemas/StocktakeLine" + }, + "readOnly": true } }, "required": [ - "ends_at", - "member_id", - "starts_at" + "approval_required", + "id", + "lines", + "makerspace", + "started_at" ] }, - "StaffInvitation": { + "StocktakeCreate": { + "type": "object", + "properties": { + "blind": { + "type": "boolean" + }, + "container_id": { + "type": "integer", + "nullable": true + }, + "notes": { + "type": "string" + } + } + }, + "StocktakeLine": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "email": { - "type": "string", - "readOnly": true + "product": { + "type": "integer", + "nullable": true }, - "makerspace": { - "type": "integer" + "asset": { + "type": "integer", + "nullable": true }, - "role": { - "type": "integer" + "container": { + "type": "integer", + "nullable": true }, - "role_name": { - "type": "string", - "readOnly": true + "expected_quantity": { + "type": "integer", + "readOnly": true, + "nullable": true }, - "expires_at": { - "type": "string", - "format": "date-time" + "counted_quantity": { + "type": "integer", + "maximum": 2147483647, + "minimum": -2147483648 }, - "accepted_at": { - "type": "string", - "format": "date-time", + "variance_quantity": { + "type": "integer", + "readOnly": true, "nullable": true }, - "revoked_at": { - "type": "string", - "format": "date-time", - "nullable": true + "condition": { + "$ref": "#/components/schemas/ConditionEnum" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "notes": { + "type": "string" } }, "required": [ - "created_at", - "email", - "expires_at", + "expected_quantity", "id", - "makerspace", - "role", - "role_name" + "variance_quantity" ] }, - "StaffInvitationAccept": { + "StocktakeLineInput": { "type": "object", "properties": { - "token": { - "type": "string", - "writeOnly": true, - "maxLength": 200, - "minLength": 20 + "product_id": { + "type": "integer" }, - "makerspace_slug": { - "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" + "asset_id": { + "type": "integer" }, - "password": { - "type": "string", - "writeOnly": true, - "maxLength": 1024 + "container_id": { + "type": "integer", + "nullable": true }, - "code": { - "type": "string", - "writeOnly": true, - "default": "", - "maxLength": 64 - } - }, - "required": [ - "makerspace_slug", - "password", - "token" - ] - }, - "StaffInvitationAccepted": { - "type": "object", - "properties": { - "detail": { + "counted_quantity": { + "type": "integer", + "minimum": 0 + }, + "condition": { + "allOf": [ + { + "$ref": "#/components/schemas/ConditionEnum" + } + ], + "default": "available" + }, + "notes": { "type": "string" } }, "required": [ - "detail" + "counted_quantity" ] }, - "StaffInvitationCreate": { + "StocktakeScanInput": { "type": "object", "properties": { - "email": { + "payload": { "type": "string", - "format": "email", - "maxLength": 254 - }, - "role_id": { - "type": "integer", - "minimum": 1 - }, - "expires_in_days": { - "type": "integer", - "maximum": 30, - "minimum": 1, - "default": 7 + "maxLength": 64 } }, "required": [ - "email", - "role_id" + "payload" ] }, - "StaffInvitationDelivery": { + "StocktakeScanResult": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "email": { - "type": "string", - "readOnly": true + "type": { + "$ref": "#/components/schemas/StocktakeScanResultTypeEnum" }, - "makerspace": { + "container_id": { "type": "integer" }, - "role": { + "asset_id": { "type": "integer" }, - "role_name": { - "type": "string", - "readOnly": true + "product_id": { + "type": "integer" }, - "expires_at": { - "type": "string", - "format": "date-time" + "label": { + "type": "string" }, - "accepted_at": { - "type": "string", - "format": "date-time", - "nullable": true + "code": { + "type": "string" }, - "revoked_at": { - "type": "string", - "format": "date-time", - "nullable": true + "asset_tag": { + "type": "string" }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "product": { + "type": "string" }, - "accept_url": { + "name": { "type": "string" }, - "delivery_status": { + "status": { "type": "string" } }, "required": [ - "accept_url", - "created_at", - "delivery_status", - "email", - "expires_at", - "id", - "makerspace", - "role", - "role_name" + "type" + ] + }, + "StocktakeScanResultTypeEnum": { + "enum": [ + "box", + "asset", + "product" + ], + "type": "string", + "description": "* `box` - box\n* `asset` - asset\n* `product` - product" + }, + "StocktakeStatusEnum": { + "enum": [ + "draft", + "counting", + "completed", + "approved", + "applied", + "cancelled" + ], + "type": "string", + "description": "* `draft` - Draft\n* `counting` - Counting\n* `completed` - Completed\n* `approved` - Approved\n* `applied` - Applied\n* `cancelled` - Cancelled" + }, + "StrategyEnum": { + "enum": [ + "sum", + "group_sum", + "row_union", + "weighted_rate", + "global_rank", + "distinct_person" + ], + "type": "string", + "description": "* `sum` - sum\n* `group_sum` - group_sum\n* `row_union` - row_union\n* `weighted_rate` - weighted_rate\n* `global_rank` - global_rank\n* `distinct_person` - distinct_person" + }, + "StreamEnum": { + "enum": [ + "hardware", + "printing", + "events", + "bookings", + "maintenance", + "membership", + "devices", + "payments", + "stock", + "certifications" + ], + "type": "string", + "description": "* `hardware` - Hardware\n* `printing` - Printing\n* `events` - Events\n* `bookings` - Bookings\n* `maintenance` - Maintenance\n* `membership` - Membership\n* `devices` - Devices\n* `payments` - Payments\n* `stock` - Stock and procurement\n* `certifications` - Certifications" + }, + "StripeConnectOnboarding": { + "type": "object", + "properties": { + "authorize_url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "authorize_url" ] }, - "StaffMembership": { + "StripeMobilePaymentIntentResponse": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "user": { - "$ref": "#/components/schemas/User" + "vendor": { + "$ref": "#/components/schemas/StripeMobilePaymentIntentResponseVendorEnum" }, - "makerspace_id": { + "payment_id": { "type": "integer" }, - "makerspace_slug": { - "type": "string", - "pattern": "^[-a-zA-Z0-9_]+$" + "client_secret": { + "type": "string" }, - "role": { - "$ref": "#/components/schemas/Role827Enum" + "publishable_key": { + "type": "string" }, - "created_at": { + "customer_id": { "type": "string", - "format": "date-time", - "readOnly": true + "nullable": true + }, + "ephemeral_key": { + "type": "string", + "nullable": true } }, "required": [ - "created_at", - "id", - "makerspace_id", - "makerspace_slug", - "user" + "client_secret", + "payment_id", + "publishable_key", + "vendor" ] }, - "StaffPayment": { + "StripeMobilePaymentIntentResponseVendorEnum": { + "enum": [ + "stripe" + ], + "type": "string", + "description": "* `stripe` - stripe" + }, + "SubdomainRequest": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "subject_type": { - "$ref": "#/components/schemas/SubjectTypeEnum" - }, - "subject_label": { + "requested_label": { "type": "string", - "readOnly": true + "maxLength": 63 }, "status": { - "$ref": "#/components/schemas/Status66aEnum" - }, - "amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "currency": { - "type": "string", - "pattern": "^[a-z]{3}$", - "maxLength": 3 - }, - "checkout_url": { - "type": "string", - "readOnly": true - }, - "online_payment_available": { - "type": "boolean", - "description": "Whether offering to pay online is honest for this charge.\n\nResolved once per makerspace by the view and passed in: doing it per row would\nre-read settings and credentials for every charge in the list. Falls back to a\nlive check only when a caller has not supplied it.", - "readOnly": true - }, - "settlement": { "allOf": [ { - "$ref": "#/components/schemas/MemberSettlement" + "$ref": "#/components/schemas/StatusE94Enum" } ], - "nullable": true, "readOnly": true }, + "note": { + "type": "string", + "readOnly": true + }, + "decided_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, "created_at": { "type": "string", "format": "date-time", @@ -102782,896 +126171,871 @@ } }, "required": [ - "amount", - "checkout_url", "created_at", - "currency", + "decided_at", "id", - "online_payment_available", - "settlement", - "subject_label", - "subject_type" + "note", + "requested_label", + "status" ] }, - "StaffPaymentSummary": { + "SubdomainRequestError": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/Status66aEnum" - } - ], - "readOnly": true - }, - "amount": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "readOnly": true - }, - "currency": { - "type": "string", - "readOnly": true + "detail": { + "type": "string" } }, "required": [ - "amount", - "currency", - "id", - "status" + "detail" ] }, - "StaffSettlement": { + "SubjectExport": { "type": "object", - "description": "The receipt that currently stands for a charge settled at the desk.\n\n`corrected` says an earlier receipt was replaced through amend-settlement; the chain\nitself stays in the ledger and the audit log.", "properties": { "id": { - "type": "integer", + "type": "string", + "format": "uuid", "readOnly": true }, - "method": { + "status": { "allOf": [ { - "$ref": "#/components/schemas/Method2edEnum" + "$ref": "#/components/schemas/StatusE1dEnum" } ], "readOnly": true }, - "reference": { + "failure_code": { + "readOnly": true, + "oneOf": [ + { + "$ref": "#/components/schemas/FailureCodeEnum" + }, + { + "$ref": "#/components/schemas/BlankEnum" + } + ] + }, + "failure_detail": { "type": "string", "readOnly": true }, - "received_at": { + "expires_at": { "type": "string", "format": "date-time", "readOnly": true }, - "recorded_at": { + "created_at": { "type": "string", "format": "date-time", "readOnly": true }, - "corrected": { - "type": "boolean", + "completed_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "manifest": { "readOnly": true } }, "required": [ - "corrected", + "completed_at", + "created_at", + "expires_at", + "failure_code", + "failure_detail", "id", - "method", - "received_at", - "recorded_at", - "reference" + "manifest", + "status" ] }, - "StageEnum": { - "enum": [ - "requested", - "claimed", - "preflight", - "quiesced", - "db_restoring", - "objects_restoring", - "validating", - "completed", - "restored_quarantined", - "rolling_back", - "failed", - "aborted" - ], - "type": "string", - "description": "* `requested` - Requested\n* `claimed` - Claimed\n* `preflight` - Preflight\n* `quiesced` - Quiesced\n* `db_restoring` - Database restoring\n* `objects_restoring` - Objects restoring\n* `validating` - Validating\n* `completed` - Completed\n* `restored_quarantined` - Restored quarantined\n* `rolling_back` - Rolling back\n* `failed` - Failed\n* `aborted` - Aborted" - }, - "State31eEnum": { - "enum": [ - "active", - "expired", - "revoked", - "redeemed" - ], - "type": "string", - "description": "* `active` - active\n* `expired` - expired\n* `revoked` - revoked\n* `redeemed` - redeemed" - }, - "State9efEnum": { - "enum": [ - "requested", - "invited", - "awaiting_vouches", - "awaiting_payment", - "expired", - "canceled", - "active", - "revoked" - ], - "type": "string", - "description": "* `requested` - Requested\n* `invited` - Invited\n* `awaiting_vouches` - Awaiting vouches\n* `awaiting_payment` - Awaiting payment\n* `expired` - Expired\n* `canceled` - Canceled\n* `active` - Active\n* `revoked` - Revoked" - }, - "StationPin": { + "SubjectExportList": { "type": "object", "properties": { - "pin": { + "count": { + "type": "integer" + }, + "next": { "type": "string", - "writeOnly": true, - "pattern": "^\\d{8}$" - } - }, - "required": [ - "pin" - ] - }, - "StationReveal": { - "type": "object", - "properties": { - "current_password": { + "format": "uri", + "nullable": true + }, + "previous": { "type": "string", - "writeOnly": true + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SubjectExport" + } } }, "required": [ - "current_password" + "count", + "next", + "previous", + "results" ] }, - "StationRevealResponse": { + "SubjectTypeEnum": { + "enum": [ + "machine_service_request", + "booking", + "event_registration", + "makerspace_membership", + "membership_term", + "membership_request", + "loan_deposit", + "loan_late_fee" + ], + "type": "string", + "description": "* `machine_service_request` - Machine service request\n* `booking` - Booking\n* `event_registration` - Event registration\n* `makerspace_membership` - Makerspace membership\n* `membership_term` - Membership renewal\n* `membership_request` - Membership admission\n* `loan_deposit` - Loan deposit\n* `loan_late_fee` - Loan late fee" + }, + "Suppression": { "type": "object", "properties": { - "pin": { - "type": "string" + "id": { + "type": "integer", + "readOnly": true }, - "version": { - "type": "integer" + "address_hash": { + "type": "string", + "readOnly": true + }, + "reason": { + "allOf": [ + { + "$ref": "#/components/schemas/Reason870Enum" + } + ], + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true } }, "required": [ - "pin", - "version" + "address_hash", + "created_at", + "id", + "reason" ] }, - "StationRotation": { + "SuppressionPage": { "type": "object", "properties": { - "pin": { - "type": "string" + "count": { + "type": "integer" }, - "public_token": { + "next": { "type": "string", - "format": "uuid" - }, - "version": { - "type": "integer" + "format": "uri", + "nullable": true }, - "station_url": { + "previous": { "type": "string", - "format": "uri" + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Suppression" + } } }, "required": [ - "pin", - "public_token", - "station_url", - "version" + "count", + "next", + "previous", + "results" ] }, - "StationStatus": { + "SuppressionWrite": { "type": "object", "properties": { - "configured": { - "type": "boolean" - }, - "enabled": { - "type": "boolean" - }, - "public_token": { - "type": "string", - "format": "uuid" - }, - "version": { - "type": "integer" - }, - "station_url": { + "address": { "type": "string", - "format": "uri" + "format": "email", + "writeOnly": true, + "maxLength": 254 }, - "rotated_at": { - "type": "string", - "format": "date-time" + "reason": { + "$ref": "#/components/schemas/Reason870Enum" } }, "required": [ - "configured" + "address", + "reason" ] }, - "Status00eEnum": { - "enum": [ - "ok", - "warn", - "error" - ], - "type": "string", - "description": "* `ok` - ok\n* `warn` - warn\n* `error` - error" - }, - "Status2caEnum": { - "enum": [ - "pending", - "confirmed", - "rejected", - "cancelled", - "completed", - "no_show" - ], - "type": "string", - "description": "* `pending` - Pending\n* `confirmed` - Confirmed\n* `rejected` - Rejected\n* `cancelled` - Cancelled\n* `completed` - Completed\n* `no_show` - No-show" - }, - "Status37fEnum": { - "enum": [ - "active", - "revoked" - ], - "type": "string", - "description": "* `active` - Active\n* `revoked` - Revoked" - }, - "Status66aEnum": { - "enum": [ - "pending", - "paid_online", - "paid_offline", - "waived", - "canceled" - ], - "type": "string", - "description": "* `pending` - Pending\n* `paid_online` - Paid online\n* `paid_offline` - Paid offline\n* `waived` - Waived\n* `canceled` - Canceled" - }, - "Status83eEnum": { - "enum": [ - "ok", - "warn", - "error", - "unknown" - ], - "type": "string", - "description": "* `ok` - ok\n* `warn` - warn\n* `error` - error\n* `unknown` - unknown" - }, - "StatusA39Enum": { - "enum": [ - "pending", - "verified", - "failed" - ], - "type": "string", - "description": "* `pending` - Pending\n* `verified` - Verified\n* `failed` - Failed" - }, - "StatusB9dEnum": { - "enum": [ - "invited", - "accepted", - "declined" - ], - "type": "string", - "description": "* `invited` - Invited\n* `accepted` - Accepted\n* `declined` - Declined" - }, - "StatusE1dEnum": { - "enum": [ - "pending", - "running", - "available", - "failed" - ], - "type": "string", - "description": "* `pending` - Pending\n* `running` - Running\n* `available` - Available\n* `failed` - Failed" - }, - "StatusE90Enum": { - "enum": [ - "published" - ], - "type": "string", - "description": "* `published` - published" - }, - "StatusE94Enum": { + "Surface356Enum": { "enum": [ - "pending", - "approved", - "rejected" + "member", + "staff" ], "type": "string", - "description": "* `pending` - Pending\n* `approved` - Approved\n* `rejected` - Rejected" + "description": "* `member` - Member\n* `staff` - Staff" }, - "StatusFbbEnum": { + "Surface817Enum": { "enum": [ - "draft", - "published", - "cancelled", - "completed" + "member", + "staff" ], "type": "string", - "description": "* `draft` - Draft\n* `published` - Published\n* `cancelled` - Cancelled\n* `completed` - Completed" + "description": "* `member` - member\n* `staff` - staff" }, - "StockTransfer": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer" - }, - "source_container": { - "type": "integer", - "nullable": true - }, - "destination_container": { - "type": "integer", - "nullable": true - }, - "source_makerspace": { - "type": "integer", - "nullable": true - }, - "destination_makerspace": { - "type": "integer", - "nullable": true - }, - "created_by": { - "type": "integer", - "nullable": true - }, - "reason": { - "type": "string" - }, - "status": { - "$ref": "#/components/schemas/StockTransferStatusEnum" - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true - }, - "applied_at": { - "type": "string", - "format": "date-time", - "nullable": true + "TakenItemsReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "lines": { + "typed_rows": { "type": "array", "items": { - "$ref": "#/components/schemas/StockTransferLine" - }, - "readOnly": true + "$ref": "#/components/schemas/TakenItemsReportRow" + } } }, "required": [ - "created_at", - "id", - "lines", - "makerspace", - "reason" + "rows", + "typed_rows" ] }, - "StockTransferCreate": { + "TakenItemsReportRow": { "type": "object", "properties": { - "source_container_id": { - "type": "integer", - "nullable": true - }, - "destination_container_id": { - "type": "integer", - "nullable": true - }, - "destination_makerspace_id": { - "type": "integer", - "nullable": true + "makerspace_id": { + "type": "integer" }, - "reason": { + "product": { "type": "string" }, - "lines": { - "type": "array", - "items": { - "$ref": "#/components/schemas/StockTransferLineInput" - } + "issued_quantity": { + "type": "integer" } }, "required": [ - "lines", - "reason" + "issued_quantity", + "product" ] }, - "StockTransferLine": { + "TargetEnum": { + "enum": [ + "spool", + "printer" + ], + "type": "string", + "description": "* `spool` - spool\n* `printer` - printer" + }, + "TelegramTestAlert": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "product": { - "type": "integer", - "nullable": true - }, - "asset": { - "type": "integer", - "nullable": true - }, - "quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 - }, - "from_status": { - "type": "string", - "maxLength": 32 + "makerspace_id": { + "type": "integer" }, - "to_status": { + "message": { "type": "string", - "maxLength": 32 - }, - "notes": { - "type": "string" + "default": "Space Works test alert." } }, "required": [ - "id" + "makerspace_id" ] }, - "StockTransferLineInput": { + "TelegramWebhook": { "type": "object", "properties": { - "product_id": { - "type": "integer" - }, - "asset_id": { + "update_id": { "type": "integer" }, - "quantity": { - "type": "integer", - "minimum": 1, - "default": 1 - }, - "from_status": { - "oneOf": [ - { - "$ref": "#/components/schemas/InventoryAssetStatusEnum" - }, - { - "$ref": "#/components/schemas/BlankEnum" - } - ] + "callback_query": { + "type": "object", + "additionalProperties": {} + } + } + }, + "Telemetry": { + "type": "object", + "properties": { + "external_job_id": { + "type": "string", + "maxLength": 120 }, - "to_status": { - "oneOf": [ - { - "$ref": "#/components/schemas/InventoryAssetStatusEnum" - }, - { - "$ref": "#/components/schemas/BlankEnum" - } - ] + "telemetry_state": { + "$ref": "#/components/schemas/TelemetryStateEnum" }, - "notes": { - "type": "string" + "progress_percent": { + "type": "integer", + "maximum": 100, + "minimum": 0 } - } + }, + "required": [ + "external_job_id", + "progress_percent", + "telemetry_state" + ] }, - "StockTransferStatusEnum": { + "TelemetryStateEnum": { "enum": [ - "applied", - "cancelled" + "idle", + "running", + "paused" ], "type": "string", - "description": "* `applied` - Applied\n* `cancelled` - Cancelled" + "description": "* `idle` - idle\n* `running` - running\n* `paused` - paused" }, - "Stocktake": { + "TemplateEnum": { + "enum": [ + "quick_pulse", + "event_review", + "session_speaker" + ], + "type": "string", + "description": "* `quick_pulse` - quick_pulse\n* `event_review` - event_review\n* `session_speaker` - session_speaker" + }, + "TenantBootstrap": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, "makerspace": { - "type": "integer" + "$ref": "#/components/schemas/TenantBootstrapMakerspace" }, - "container": { - "type": "integer", - "nullable": true + "frontend": { + "$ref": "#/components/schemas/TenantBootstrapFrontend" }, - "status": { - "$ref": "#/components/schemas/StocktakeStatusEnum" + "modules": { + "type": "array", + "items": { + "type": "string" + } }, - "started_by": { - "type": "integer", - "nullable": true + "edition": { + "type": "string" }, - "approved_by": { - "type": "integer", - "nullable": true + "features": { + "type": "array", + "items": { + "type": "string" + } }, - "started_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "workflows": { + "type": "array", + "items": { + "type": "string" + } }, - "completed_at": { - "type": "string", - "format": "date-time", - "nullable": true + "theme": {}, + "branding": {}, + "email_enabled": { + "type": "boolean" }, - "approved_at": { - "type": "string", - "format": "date-time", - "nullable": true + "messaging": { + "$ref": "#/components/schemas/TenantMessagingAvailability" }, - "notes": { + "public_api": { + "$ref": "#/components/schemas/TenantBootstrapPublicApi" + } + }, + "required": [ + "branding", + "edition", + "email_enabled", + "features", + "frontend", + "makerspace", + "messaging", + "modules", + "public_api", + "theme", + "workflows" + ] + }, + "TenantBootstrapFrontend": { + "type": "object", + "properties": { + "type": { "type": "string" }, - "blind": { - "type": "boolean" - }, - "variance_threshold": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0, - "nullable": true - }, - "approval_required": { - "type": "boolean", - "readOnly": true + "hostname": { + "type": "string" }, - "lines": { + "allowed_origins": { "type": "array", "items": { - "$ref": "#/components/schemas/StocktakeLine" - }, - "readOnly": true + "type": "string" + } } }, "required": [ - "approval_required", - "id", - "lines", - "makerspace", - "started_at" + "allowed_origins", + "hostname", + "type" ] }, - "StocktakeCreate": { + "TenantBootstrapMakerspace": { "type": "object", "properties": { - "blind": { - "type": "boolean" + "id": { + "type": "integer" }, - "container_id": { - "type": "integer", - "nullable": true + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "public_code": { + "type": "string" + }, + "location": { + "type": "string" + }, + "timezone": { + "type": "string" + }, + "opening_hours": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "required": [ + "weekly", + "exceptions" + ], + "additionalProperties": false, + "properties": { + "weekly": { + "oneOf": [ + { + "type": "object", + "maxProperties": 0 + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "mon", + "tue", + "wed", + "thu", + "fri", + "sat", + "sun" + ], + "properties": { + "mon": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "tue": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "wed": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "thu": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "fri": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sat": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "sun": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + } + } + } + ] + }, + "exceptions": { + "type": "object", + "maxProperties": 366, + "additionalProperties": { + "type": "array", + "maxItems": 24, + "items": { + "type": "object", + "required": [ + "start", + "end" + ], + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" + }, + "end": { + "type": "string", + "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" + } + } + } + }, + "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." + } + } + } + ], + "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." }, - "notes": { - "type": "string" - } - } - }, - "StocktakeLine": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "readOnly": true + "map_url": { + "oneOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "product": { - "type": "integer", - "nullable": true + "geofence_enabled": { + "type": "boolean" }, - "asset": { - "type": "integer", + "logo_url": { + "type": "string", "nullable": true }, - "container": { - "type": "integer", + "cover_image_url": { + "type": "string", "nullable": true }, - "expected_quantity": { - "type": "integer", - "readOnly": true, - "nullable": true + "public_stats_enabled": { + "type": "boolean" }, - "counted_quantity": { - "type": "integer", - "maximum": 2147483647, - "minimum": -2147483648 + "public_site_enabled": { + "type": "boolean" }, - "variance_quantity": { - "type": "integer", - "readOnly": true, - "nullable": true + "public_inventory_enabled": { + "type": "boolean" }, - "condition": { - "$ref": "#/components/schemas/ConditionEnum" + "public_landing_page": { + "$ref": "#/components/schemas/TenantBootstrapMakerspacePublicLandingPageEnum" }, - "notes": { - "type": "string" + "membership_policy": { + "$ref": "#/components/schemas/MembershipPolicyEnum" + }, + "request_access": { + "$ref": "#/components/schemas/RequestAccessEnum" } }, "required": [ - "expected_quantity", + "cover_image_url", "id", - "variance_quantity" + "location", + "logo_url", + "map_url", + "membership_policy", + "name", + "opening_hours", + "public_code", + "public_inventory_enabled", + "public_landing_page", + "public_site_enabled", + "public_stats_enabled", + "slug", + "timezone" ] }, - "StocktakeLineInput": { + "TenantBootstrapMakerspacePublicLandingPageEnum": { + "enum": [ + "auto", + "inventory", + "events", + "machines", + "bookings", + "membership" + ], + "type": "string", + "description": "* `auto` - auto\n* `inventory` - inventory\n* `events` - events\n* `machines` - machines\n* `bookings` - bookings\n* `membership` - membership" + }, + "TenantBootstrapPublicApi": { "type": "object", "properties": { - "product_id": { - "type": "integer" - }, - "asset_id": { - "type": "integer" - }, - "container_id": { - "type": "integer", - "nullable": true - }, - "counted_quantity": { - "type": "integer", - "minimum": 0 + "base_url": { + "type": "string" }, - "condition": { - "allOf": [ - { - "$ref": "#/components/schemas/ConditionEnum" - } - ], - "default": "available" + "publishable_key": { + "type": "string" }, - "notes": { + "inventory_path": { "type": "string" } }, "required": [ - "counted_quantity" - ] - }, - "StocktakeScanInput": { - "type": "object", - "properties": { - "payload": { - "type": "string", - "maxLength": 64 - } - }, - "required": [ - "payload" + "base_url", + "inventory_path", + "publishable_key" ] }, - "StocktakeScanResult": { + "TenantMessagingAvailability": { "type": "object", "properties": { - "type": { - "$ref": "#/components/schemas/TypeEnum" - }, - "container_id": { - "type": "integer" - }, - "asset_id": { - "type": "integer" - }, - "product_id": { - "type": "integer" - }, - "label": { - "type": "string" - }, - "code": { - "type": "string" - }, - "asset_tag": { - "type": "string" - }, - "product": { - "type": "string" - }, - "name": { - "type": "string" + "sms": { + "type": "boolean" }, - "status": { - "type": "string" + "whatsapp": { + "type": "boolean" } }, "required": [ - "type" + "sms", + "whatsapp" ] }, - "StocktakeStatusEnum": { - "enum": [ - "draft", - "counting", - "completed", - "approved", - "applied", - "cancelled" - ], - "type": "string", - "description": "* `draft` - Draft\n* `counting` - Counting\n* `completed` - Completed\n* `approved` - Approved\n* `applied` - Applied\n* `cancelled` - Cancelled" - }, - "StrategyEnum": { - "enum": [ - "sum", - "group_sum", - "row_union", - "weighted_rate", - "global_rank", - "distinct_person" - ], - "type": "string", - "description": "* `sum` - sum\n* `group_sum` - group_sum\n* `row_union` - row_union\n* `weighted_rate` - weighted_rate\n* `global_rank` - global_rank\n* `distinct_person` - distinct_person" - }, - "StreamEnum": { + "TextAlignEnum": { "enum": [ - "hardware", - "printing", - "events", - "bookings", - "maintenance", - "membership", - "devices", - "payments", - "stock", - "certifications" + "left", + "center" ], "type": "string", - "description": "* `hardware` - Hardware\n* `printing` - Printing\n* `events` - Events\n* `bookings` - Bookings\n* `maintenance` - Maintenance\n* `membership` - Membership\n* `devices` - Devices\n* `payments` - Payments\n* `stock` - Stock and procurement\n* `certifications` - Certifications" - }, - "StripeConnectOnboarding": { - "type": "object", - "properties": { - "authorize_url": { - "type": "string", - "format": "uri" - } - }, - "required": [ - "authorize_url" - ] + "description": "* `left` - left\n* `center` - center" }, - "SubdomainRequest": { + "Ticket": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "requested_label": { + "name": { "type": "string", - "maxLength": 63 + "readOnly": true }, - "status": { + "amount": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "readOnly": true + }, + "audience": { "allOf": [ { - "$ref": "#/components/schemas/StatusE94Enum" + "$ref": "#/components/schemas/AudienceBe5Enum" } ], "readOnly": true }, - "note": { - "type": "string", + "capacity": { + "type": "integer", "readOnly": true }, - "decided_at": { + "sales_start_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "created_at": { + "sales_end_at": { "type": "string", "format": "date-time", + "readOnly": true, + "nullable": true + }, + "is_active": { + "type": "boolean", "readOnly": true } }, "required": [ - "created_at", - "decided_at", + "amount", + "audience", + "capacity", "id", - "note", - "requested_label", - "status" - ] - }, - "SubdomainRequestError": { - "type": "object", - "properties": { - "detail": { - "type": "string" - } - }, - "required": [ - "detail" + "is_active", + "name", + "sales_end_at", + "sales_start_at" ] }, - "SubjectExport": { + "TicketInput": { "type": "object", "properties": { - "id": { + "name": { "type": "string", - "format": "uuid", - "readOnly": true - }, - "status": { - "allOf": [ - { - "$ref": "#/components/schemas/StatusE1dEnum" - } - ], - "readOnly": true - }, - "failure_code": { - "readOnly": true, - "oneOf": [ - { - "$ref": "#/components/schemas/FailureCodeEnum" - }, - { - "$ref": "#/components/schemas/BlankEnum" - } - ] + "maxLength": 100 }, - "failure_detail": { + "amount": { "type": "string", - "readOnly": true + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" }, - "expires_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "audience": { + "$ref": "#/components/schemas/AudienceBe5Enum" }, - "created_at": { + "capacity": { + "type": "integer", + "maximum": 2147483647, + "minimum": 0 + }, + "sales_start_at": { "type": "string", "format": "date-time", - "readOnly": true + "nullable": true }, - "completed_at": { + "sales_end_at": { "type": "string", "format": "date-time", - "readOnly": true, "nullable": true - }, - "manifest": { - "readOnly": true } }, "required": [ - "completed_at", - "created_at", - "expires_at", - "failure_code", - "failure_detail", - "id", - "manifest", - "status" + "name" ] }, - "SubjectExportList": { + "TicketList": { "type": "object", "properties": { "count": { @@ -103690,7 +127054,7 @@ "results": { "type": "array", "items": { - "$ref": "#/components/schemas/SubjectExport" + "$ref": "#/components/schemas/Ticket" } } }, @@ -103701,537 +127065,210 @@ "results" ] }, - "SubjectTypeEnum": { - "enum": [ - "machine_service_request", - "booking", - "event_registration", - "makerspace_membership", - "membership_term", - "membership_request", - "loan_deposit", - "loan_late_fee" - ], - "type": "string", - "description": "* `machine_service_request` - Machine service request\n* `booking` - Booking\n* `event_registration` - Event registration\n* `makerspace_membership` - Makerspace membership\n* `membership_term` - Membership renewal\n* `membership_request` - Membership admission\n* `loan_deposit` - Loan deposit\n* `loan_late_fee` - Loan late fee" - }, - "Suppression": { + "TicketPublicFields": { "type": "object", "properties": { - "id": { - "type": "integer", + "status": { + "type": "string", "readOnly": true }, - "address_hash": { + "title": { "type": "string", "readOnly": true }, - "reason": { - "allOf": [ - { - "$ref": "#/components/schemas/Reason870Enum" - } - ], + "description": { + "type": "string", "readOnly": true }, - "created_at": { + "starts_at": { "type": "string", "format": "date-time", "readOnly": true - } - }, - "required": [ - "address_hash", - "created_at", - "id", - "reason" - ] - }, - "SuppressionPage": { - "type": "object", - "properties": { - "count": { - "type": "integer" }, - "next": { + "ends_at": { "type": "string", - "format": "uri", - "nullable": true + "format": "date-time", + "readOnly": true }, - "previous": { + "location": { "type": "string", - "format": "uri", - "nullable": true + "readOnly": true }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Suppression" - } - } - }, - "required": [ - "count", - "next", - "previous", - "results" - ] - }, - "SuppressionWrite": { - "type": "object", - "properties": { - "address": { + "public_token": { "type": "string", - "format": "email", - "writeOnly": true, - "maxLength": 254 - }, - "reason": { - "$ref": "#/components/schemas/Reason870Enum" - } - }, - "required": [ - "address", - "reason" - ] - }, - "Surface356Enum": { - "enum": [ - "member", - "staff" - ], - "type": "string", - "description": "* `member` - Member\n* `staff` - Staff" - }, - "TakenItemsReport": { - "type": "object", - "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } - }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TakenItemsReportRow" - } - } - }, - "required": [ - "rows", - "typed_rows" - ] - }, - "TakenItemsReportRow": { - "type": "object", - "properties": { - "makerspace_id": { - "type": "integer" - }, - "product": { - "type": "string" - }, - "issued_quantity": { - "type": "integer" - } - }, - "required": [ - "issued_quantity", - "product" - ] - }, - "TargetEnum": { - "enum": [ - "spool", - "printer" - ], - "type": "string", - "description": "* `spool` - spool\n* `printer` - printer" - }, - "TelegramTestAlert": { - "type": "object", - "properties": { - "makerspace_id": { - "type": "integer" + "format": "uuid", + "readOnly": true }, - "message": { + "timezone_name": { "type": "string", - "default": "Space Works test alert." + "readOnly": true } }, "required": [ - "makerspace_id" - ] - }, - "TelegramWebhook": { - "type": "object", - "properties": { - "update_id": { - "type": "integer" - }, - "callback_query": { - "type": "object", - "additionalProperties": {} - } - } + "description", + "ends_at", + "location", + "public_token", + "starts_at", + "status", + "timezone_name", + "title" + ] }, - "Telemetry": { + "TimelineActor": { "type": "object", "properties": { - "external_job_id": { - "type": "string", - "maxLength": 120 - }, - "telemetry_state": { - "$ref": "#/components/schemas/TelemetryStateEnum" + "username": { + "type": "string" }, - "progress_percent": { - "type": "integer", - "maximum": 100, - "minimum": 0 + "role": { + "type": "string" } }, "required": [ - "external_job_id", - "progress_percent", - "telemetry_state" + "role", + "username" ] }, - "TelemetryStateEnum": { - "enum": [ - "idle", - "running", - "paused" - ], - "type": "string", - "description": "* `idle` - idle\n* `running` - running\n* `paused` - paused" - }, - "TenantBootstrap": { + "TimelineEvent": { "type": "object", "properties": { - "makerspace": { - "$ref": "#/components/schemas/TenantBootstrapMakerspace" - }, - "frontend": { - "$ref": "#/components/schemas/TenantBootstrapFrontend" - }, - "modules": { - "type": "array", - "items": { - "type": "string" - } + "kind": { + "type": "string", + "description": "Event kind. `audit` events carry `detail.action`, `detail.action_label`, projected `detail.meta`, `detail.meta_redacted` and `detail.target_link`." }, - "edition": { - "type": "string" + "at": { + "type": "string", + "format": "date-time" }, - "features": { - "type": "array", - "items": { - "type": "string" - } + "actor": { + "allOf": [ + { + "$ref": "#/components/schemas/TimelineActor" + } + ], + "nullable": true }, - "workflows": { - "type": "array", - "items": { - "type": "string" - } + "detail": { + "type": "object", + "additionalProperties": {} }, - "theme": {}, - "branding": {}, - "email_enabled": { - "type": "boolean" + "evidence_id": { + "type": "integer", + "nullable": true }, - "public_api": { - "$ref": "#/components/schemas/TenantBootstrapPublicApi" + "evidence_retention": { + "allOf": [ + { + "$ref": "#/components/schemas/EvidenceRetention" + } + ], + "nullable": true } }, "required": [ - "branding", - "edition", - "email_enabled", - "features", - "frontend", - "makerspace", - "modules", - "public_api", - "theme", - "workflows" + "actor", + "at", + "detail", + "evidence_id", + "kind" ] }, - "TenantBootstrapFrontend": { + "TimingPolicy": { "type": "object", "properties": { - "type": { - "type": "string" + "quiet_start": { + "type": "string", + "format": "time" }, - "hostname": { - "type": "string" + "quiet_end": { + "type": "string", + "format": "time" }, - "allowed_origins": { + "channels": { "type": "array", "items": { - "type": "string" - } + "$ref": "#/components/schemas/ChannelsEnum" + }, + "maxItems": 9 + }, + "digest_at": { + "type": "string", + "format": "time" + }, + "daily_digest_default": { + "type": "boolean" + }, + "bounce_secret": { + "writeOnly": true, + "oneOf": [ + { + "type": "string", + "maxLength": 200, + "minLength": 32 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "bounce_configured": { + "type": "boolean", + "readOnly": true } }, "required": [ - "allowed_origins", - "hostname", - "type" + "bounce_configured", + "channels", + "digest_at", + "quiet_end", + "quiet_start" ] }, - "TenantBootstrapMakerspace": { + "ToBuyItem": { "type": "object", "properties": { "id": { - "type": "integer" + "type": "integer", + "readOnly": true }, - "name": { - "type": "string" + "makerspace": { + "type": "integer", + "readOnly": true }, - "slug": { - "type": "string" + "kind": { + "allOf": [ + { + "$ref": "#/components/schemas/ToBuyItemKindEnum" + } + ], + "readOnly": true }, - "public_code": { - "type": "string" + "machine_type": { + "type": "integer", + "nullable": true }, - "location": { - "type": "string" + "machine_type_name": { + "type": "string", + "readOnly": true, + "nullable": true }, - "timezone": { - "type": "string" + "name": { + "type": "string", + "maxLength": 200 }, - "opening_hours": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "required": [ - "weekly", - "exceptions" - ], - "additionalProperties": false, - "properties": { - "weekly": { - "oneOf": [ - { - "type": "object", - "maxProperties": 0 - }, - { - "type": "object", - "additionalProperties": false, - "required": [ - "mon", - "tue", - "wed", - "thu", - "fri", - "sat", - "sun" - ], - "properties": { - "mon": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "tue": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "wed": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "thu": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "fri": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sat": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "sun": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - } - } - } - ] - }, - "exceptions": { - "type": "object", - "maxProperties": 366, - "additionalProperties": { - "type": "array", - "maxItems": 24, - "items": { - "type": "object", - "required": [ - "start", - "end" - ], - "additionalProperties": false, - "properties": { - "start": { - "type": "string", - "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" - }, - "end": { - "type": "string", - "pattern": "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]|24:00)$" - } - } - } - }, - "description": "YYYY-MM-DD keys; each value replaces that date's weekly hours." - } - } - } - ], - "description": "Empty object means always open. Empty weekly hours mean always open except dated overrides. Empty interval lists mean closed. Sorted intervals use local wall time; split overnight hours across days." + "quantity": { + "type": "integer", + "minimum": 1, + "default": 1 }, - "map_url": { + "link": { "oneOf": [ { "type": "string", - "format": "uri" + "format": "uri", + "maxLength": 200 }, { "type": "string", @@ -104239,855 +127276,1219 @@ } ] }, - "geofence_enabled": { + "status": { + "$ref": "#/components/schemas/ToBuyItemStatusEnum" + }, + "estimated_unit_cost": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "vendor_name": { + "type": "string", + "maxLength": 200 + }, + "actual_unit_cost": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", + "nullable": true + }, + "purchaser": { + "type": "integer", + "readOnly": true + }, + "purchaser_username": { + "type": "string", + "readOnly": true + }, + "ordered_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "received_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "moved_to_inventory_at": { + "type": "string", + "format": "date-time", + "readOnly": true, + "nullable": true + }, + "resulting_product": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "resulting_pool": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "resulting_machine": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "source_pool": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "source_product": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "receipts": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToBuyReceipt" + }, + "readOnly": true + }, + "created_by": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "created_by_username": { + "type": "string", + "readOnly": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "readOnly": true + }, + "updated_at": { + "type": "string", + "format": "date-time", + "readOnly": true + } + }, + "required": [ + "created_at", + "created_by", + "created_by_username", + "id", + "kind", + "machine_type_name", + "makerspace", + "moved_to_inventory_at", + "name", + "ordered_at", + "purchaser", + "purchaser_username", + "receipts", + "received_at", + "resulting_machine", + "resulting_pool", + "resulting_product", + "source_pool", + "source_product", + "updated_at" + ] + }, + "ToBuyItemKindEnum": { + "enum": [ + "hardware", + "printing" + ], + "type": "string", + "description": "* `hardware` - Hardware\n* `printing` - Printing" + }, + "ToBuyItemStatusEnum": { + "enum": [ + "requested", + "approved", + "ordered", + "received", + "cancelled" + ], + "type": "string", + "description": "* `requested` - Requested\n* `approved` - Approved\n* `ordered` - Ordered\n* `received` - Received\n* `cancelled` - Cancelled" + }, + "ToBuyMachineType": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "name": { + "type": "string", + "readOnly": true + } + }, + "required": [ + "id", + "name" + ] + }, + "ToBuyMachineTypeOptions": { + "type": "object", + "description": "Options plus a SERVER-derived answer to \"must the caller pick one?\".\n\nThe console must not infer this from effective actions. A null-`assigned_role`\nlegacy membership is scope-exempt and cannot be expressed that way, and \"holds\nMANAGE_MACHINES\" is not the same question as \"is narrowed by machine scope\" -- a role\nholding MANAGE_MAKERSPACE holds both and is exempt. Deriving it here keeps one answer\non the server, the same reason the dashboard carries `scope_mode`.", + "properties": { + "machine_type_required": { "type": "boolean" }, - "logo_url": { + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToBuyMachineType" + } + } + }, + "required": [ + "machine_type_required", + "results" + ] + }, + "ToBuyReceipt": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "created_at": { "type": "string", + "format": "date-time", + "readOnly": true + }, + "uploaded_by": { + "type": "integer", + "readOnly": true, "nullable": true }, - "cover_image_url": { + "uploaded_by_username": { "type": "string", - "nullable": true + "readOnly": true + } + }, + "required": [ + "created_at", + "id", + "uploaded_by", + "uploaded_by_username" + ] + }, + "ToBuyReceiptFinalize": { + "type": "object", + "properties": { + "object_key": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "object_key" + ] + }, + "ToBuyReceiptPresign": { + "type": "object", + "properties": { + "filename": { + "type": "string", + "maxLength": 255 }, - "public_stats_enabled": { - "type": "boolean" + "content_type": { + "type": "string", + "maxLength": 100 + } + }, + "required": [ + "content_type", + "filename" + ] + }, + "ToBuyReceiptUploadResponse": { + "type": "object", + "properties": { + "object_key": { + "type": "string" }, - "public_site_enabled": { - "type": "boolean" + "upload": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "object_key", + "upload" + ] + }, + "ToBuyReceiptUrl": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "url" + ] + }, + "TopBorrowersReport": { + "type": "object", + "properties": { + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "public_inventory_enabled": { - "type": "boolean" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TopBorrowersReportRow" + } + } + }, + "required": [ + "rows", + "typed_rows" + ] + }, + "TopBorrowersReportRow": { + "type": "object", + "properties": { + "makerspace_id": { + "type": "integer" }, - "public_landing_page": { - "$ref": "#/components/schemas/TenantBootstrapMakerspacePublicLandingPageEnum" + "holder": { + "type": "string" }, - "membership_policy": { - "$ref": "#/components/schemas/MembershipPolicyEnum" + "requests": { + "type": "integer" }, - "request_access": { - "$ref": "#/components/schemas/RequestAccessEnum" + "items_borrowed": { + "type": "integer" } }, "required": [ - "cover_image_url", - "id", - "location", - "logo_url", - "map_url", - "membership_policy", - "name", - "opening_hours", - "public_code", - "public_inventory_enabled", - "public_landing_page", - "public_site_enabled", - "public_stats_enabled", - "slug", - "timezone" + "holder", + "items_borrowed", + "requests" ] }, - "TenantBootstrapMakerspacePublicLandingPageEnum": { + "TrackingModeB86Enum": { "enum": [ - "auto", - "inventory", - "events", - "machines", - "bookings", - "membership" + "quantity", + "individual" ], "type": "string", - "description": "* `auto` - auto\n* `inventory` - inventory\n* `events` - events\n* `machines` - machines\n* `bookings` - bookings\n* `membership` - membership" + "description": "* `quantity` - Quantity\n* `individual` - Individual" }, - "TenantBootstrapPublicApi": { + "TypedError": { "type": "object", "properties": { - "base_url": { + "detail": { "type": "string" }, - "publishable_key": { + "code": { "type": "string" + } + }, + "required": [ + "code", + "detail" + ] + }, + "TypedManualUsage": { + "type": "object", + "properties": { + "member": { + "type": "integer", + "minimum": 1, + "nullable": true }, - "inventory_path": { + "started_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "machine_id": { + "type": "integer", + "minimum": 1 + }, + "consumable_pool_id": { + "type": "integer", + "minimum": 1, + "nullable": true + }, + "service_request_id": { + "type": "integer", + "minimum": 1, + "nullable": true + }, + "duration_minutes": { + "type": "integer", + "minimum": 0 + }, + "outcome": { + "$ref": "#/components/schemas/TypedManualUsageOutcomeEnum" + }, + "percent_complete": { + "type": "integer", + "maximum": 100, + "minimum": 0, + "default": 100 + }, + "reason": { "type": "string" + }, + "grams": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", + "default": "0.00" + }, + "quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + }, + "metering_unit": { + "$ref": "#/components/schemas/TypedManualUsageMeteringUnitEnum" + }, + "note": { + "type": "string", + "maxLength": 255 } }, "required": [ - "base_url", - "inventory_path", - "publishable_key" + "duration_minutes", + "machine_id", + "outcome" ] }, - "TextAlignEnum": { + "TypedManualUsageMeteringUnitEnum": { "enum": [ - "left", - "center" + "minutes", + "weight", + "volume", + "length", + "count" ], "type": "string", - "description": "* `left` - left\n* `center` - center" + "description": "* `minutes` - minutes\n* `weight` - weight\n* `volume` - volume\n* `length` - length\n* `count` - count" }, - "Ticket": { + "TypedManualUsageOutcomeEnum": { + "enum": [ + "success", + "failed" + ], + "type": "string", + "description": "* `success` - success\n* `failed` - failed" + }, + "TypedManualUsageResponse": { "type": "object", "properties": { "id": { "type": "integer", "readOnly": true }, - "name": { + "machine_id": { + "type": "integer", + "readOnly": true + }, + "consumable_pool_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "service_request_id": { + "type": "integer", + "readOnly": true, + "nullable": true + }, + "duration_minutes": { + "type": "integer", + "readOnly": true + }, + "outcome": { "type": "string", "readOnly": true }, - "amount": { + "percent_complete": { + "type": "integer", + "readOnly": true + }, + "reason": { + "type": "string", + "readOnly": true + }, + "consumed_grams": { "type": "string", "format": "decimal", "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "audience": { + "metering_unit": { "allOf": [ { - "$ref": "#/components/schemas/AudienceBe5Enum" + "$ref": "#/components/schemas/TypedManualUsageResponseMeteringUnitEnum" } ], "readOnly": true }, - "capacity": { - "type": "integer", + "consumed_quantity": { + "type": "string", + "format": "decimal", + "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "sales_start_at": { + "hours": { "type": "string", - "format": "date-time", + "format": "decimal", + "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", + "readOnly": true + }, + "note": { + "type": "string", + "readOnly": true + }, + "member": { + "type": "integer", "readOnly": true, "nullable": true }, - "sales_end_at": { + "started_at": { "type": "string", "format": "date-time", "readOnly": true, "nullable": true }, - "is_active": { - "type": "boolean", + "created_at": { + "type": "string", + "format": "date-time", "readOnly": true } }, "required": [ - "amount", - "audience", - "capacity", + "consumable_pool_id", + "consumed_grams", + "consumed_quantity", + "created_at", + "duration_minutes", + "hours", "id", - "is_active", - "name", - "sales_end_at", - "sales_start_at" + "machine_id", + "member", + "metering_unit", + "note", + "outcome", + "percent_complete", + "reason", + "service_request_id", + "started_at" ] }, - "TicketInput": { + "TypedManualUsageResponseMeteringUnitEnum": { + "enum": [ + "minutes", + "weight", + "volume", + "length", + "count" + ], + "type": "string", + "description": "* `minutes` - Minutes\n* `weight` - Weight\n* `volume` - Volume\n* `length` - Length\n* `count` - Count" + }, + "UnsubscribeResult": { "type": "object", "properties": { - "name": { + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "UsageResource": { + "type": "object", + "properties": { + "key": { "type": "string", - "maxLength": 100 + "description": "Resource limit key, including daily sms and whatsapp logical messages." }, - "amount": { + "label": { + "type": "string" + }, + "used": { + "type": "integer", + "nullable": true + }, + "limit": { + "type": "integer", + "nullable": true + }, + "unit": { + "$ref": "#/components/schemas/UsageResourceUnitEnum" + } + }, + "required": [ + "key", + "label", + "limit", + "unit", + "used" + ] + }, + "UsageResourceUnitEnum": { + "enum": [ + "bytes", + "count" + ], + "type": "string", + "description": "* `bytes` - bytes\n* `count` - count" + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "readOnly": true + }, + "username": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" + "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", + "pattern": "^[\\w.@+-]+$", + "maxLength": 150 + }, + "email": { + "title": "Email address", + "oneOf": [ + { + "type": "string", + "format": "email", + "maxLength": 254 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "first_name": { + "type": "string", + "maxLength": 150 + }, + "last_name": { + "type": "string", + "maxLength": 150 }, - "audience": { - "$ref": "#/components/schemas/AudienceBe5Enum" + "role": { + "$ref": "#/components/schemas/UserRoleEnum" }, - "capacity": { - "type": "integer", - "maximum": 2147483647, - "minimum": 0 + "access_status": { + "$ref": "#/components/schemas/AccessStatusEnum" }, - "sales_start_at": { + "restriction_reason": { + "type": "string" + }, + "telegram_user_id": { "type": "string", - "format": "date-time", - "nullable": true + "maxLength": 64 }, - "sales_end_at": { + "external_checkin_user_id": { "type": "string", - "format": "date-time", - "nullable": true + "maxLength": 128 + }, + "is_active": { + "type": "boolean", + "title": "Active", + "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." } }, "required": [ - "name" + "id", + "username" ] }, - "TicketList": { + "UserRoleEnum": { + "enum": [ + "superadmin", + "space_manager", + "requester" + ], + "type": "string", + "description": "* `superadmin` - Super Admin\n* `space_manager` - Space Manager\n* `requester` - Requester" + }, + "VerificationAccepted": { "type": "object", "properties": { - "count": { + "id": { "type": "integer" }, - "next": { - "type": "string", - "format": "uri", - "nullable": true + "state": { + "type": "string" }, - "previous": { - "type": "string", - "format": "uri", - "nullable": true + "deduplicated": { + "type": "boolean" }, - "results": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Ticket" - } + "status_url": { + "type": "string" } }, "required": [ - "count", - "next", - "previous", - "results" + "deduplicated", + "id", + "state", + "status_url" ] }, - "TimelineActor": { + "VerificationCoverage": { "type": "object", "properties": { - "username": { - "type": "string" + "attempt": { + "type": "integer" }, - "role": { - "type": "string" + "chain_status": { + "$ref": "#/components/schemas/ChainStatusEnum" + }, + "anchor_status": { + "$ref": "#/components/schemas/AnchorStatusEnum" + }, + "row_count": { + "type": "integer" + }, + "mac_valid_count": { + "type": "integer" + }, + "unattested_count": { + "type": "integer" + }, + "mac_invalid_count": { + "type": "integer" + }, + "mac_unavailable_count": { + "type": "integer" + }, + "unbatched_count": { + "type": "integer" + }, + "local_batch_verified_count": { + "type": "integer" + }, + "batch_invalid_count": { + "type": "integer" + }, + "batch_unavailable_count": { + "type": "integer" + }, + "externally_anchored_count": { + "type": "integer" } }, "required": [ - "role", - "username" + "anchor_status", + "attempt", + "batch_invalid_count", + "batch_unavailable_count", + "chain_status", + "externally_anchored_count", + "local_batch_verified_count", + "mac_invalid_count", + "mac_unavailable_count", + "mac_valid_count", + "row_count", + "unattested_count", + "unbatched_count" ] }, - "TimelineEvent": { + "VerificationFilters": { "type": "object", "properties": { - "kind": { - "type": "string", - "description": "Event kind. `audit` events carry `detail.action`, `detail.action_label`, projected `detail.meta`, `detail.meta_redacted` and `detail.target_link`." + "action": { + "type": "string" }, - "at": { - "type": "string", - "format": "date-time" + "target_type": { + "type": "string" + }, + "target_id": { + "type": "string" }, "actor": { - "allOf": [ - { - "$ref": "#/components/schemas/TimelineActor" - } - ], + "type": "integer", "nullable": true }, - "detail": { - "type": "object", - "additionalProperties": {} - }, - "evidence_id": { - "type": "integer", + "start": { + "type": "string", + "format": "date-time", "nullable": true }, - "evidence_retention": { - "allOf": [ - { - "$ref": "#/components/schemas/EvidenceRetention" - } - ], + "end": { + "type": "string", + "format": "date-time", "nullable": true } }, "required": [ + "action", "actor", - "at", - "detail", - "evidence_id", - "kind" + "end", + "start", + "target_id", + "target_type" ] }, - "TimingPolicy": { + "VerificationReport": { "type": "object", "properties": { - "quiet_start": { - "type": "string", - "format": "time" + "format_version": { + "type": "integer" }, - "quiet_end": { - "type": "string", - "format": "time" + "target_makerspace_id": { + "type": "integer" }, - "channels": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ChannelsEnum" - }, - "maxItems": 7 + "imported": { + "type": "object", + "additionalProperties": {} }, - "digest_at": { - "type": "string", - "format": "time" + "resolved": { + "type": "object", + "additionalProperties": {} }, - "daily_digest_default": { - "type": "boolean" + "dropped": { + "type": "object", + "additionalProperties": {} }, - "bounce_secret": { - "writeOnly": true, - "oneOf": [ - { - "type": "string", - "maxLength": 200, - "minLength": 32 - }, - { - "type": "string", - "maxLength": 0 - } - ] + "identities_linked": { + "type": "integer" }, - "bounce_configured": { - "type": "boolean", - "readOnly": true + "identities_created": { + "type": "integer" + }, + "external_references_created": { + "type": "integer" } }, "required": [ - "bounce_configured", - "channels", - "digest_at", - "quiet_end", - "quiet_start" + "dropped", + "external_references_created", + "format_version", + "identities_created", + "identities_linked", + "imported", + "resolved", + "target_makerspace_id" ] }, - "ToBuyItem": { + "VerificationRequest": { "type": "object", + "description": "The audit-list filters, minus ``makerspace`` (it comes from the path).", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "makerspace": { - "type": "integer", - "readOnly": true - }, - "kind": { - "allOf": [ - { - "$ref": "#/components/schemas/ToBuyItemKindEnum" - } - ], - "readOnly": true - }, - "machine_type": { - "type": "integer", - "nullable": true - }, - "machine_type_name": { - "type": "string", - "readOnly": true, - "nullable": true - }, - "name": { - "type": "string", - "maxLength": 200 - }, - "quantity": { - "type": "integer", - "minimum": 1, - "default": 1 - }, - "link": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "maxLength": 200 - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "status": { - "$ref": "#/components/schemas/ToBuyItemStatusEnum" - }, - "estimated_unit_cost": { + "action": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", - "nullable": true + "maxLength": 128 }, - "vendor_name": { + "target_type": { "type": "string", "maxLength": 200 }, - "actual_unit_cost": { + "target_id": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", - "nullable": true + "maxLength": 100 }, - "purchaser": { + "actor": { "type": "integer", - "readOnly": true + "minimum": 1 }, - "purchaser_username": { + "start": { "type": "string", - "readOnly": true + "format": "date-time" }, - "ordered_at": { + "end": { "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "format": "date-time" + } + } + }, + "VerificationRun": { + "type": "object", + "properties": { + "id": { + "type": "integer" }, - "received_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "makerspace": { + "type": "integer" }, - "moved_to_inventory_at": { - "type": "string", - "format": "date-time", - "readOnly": true, - "nullable": true + "state": { + "$ref": "#/components/schemas/VerificationRunStateEnum" }, - "resulting_product": { - "type": "integer", - "readOnly": true, - "nullable": true + "terminal": { + "type": "boolean" }, - "resulting_pool": { - "type": "integer", - "readOnly": true, - "nullable": true + "snapshot_max_id": { + "type": "integer" }, - "resulting_machine": { - "type": "integer", - "readOnly": true, - "nullable": true + "filters": { + "$ref": "#/components/schemas/VerificationFilters" }, - "source_pool": { - "type": "integer", - "readOnly": true, - "nullable": true + "coverage": { + "$ref": "#/components/schemas/VerificationCoverage" }, - "source_product": { - "type": "integer", - "readOnly": true, - "nullable": true + "created_at": { + "type": "string", + "format": "date-time" }, - "receipts": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ToBuyReceipt" - }, - "readOnly": true + "updated_at": { + "type": "string", + "format": "date-time" }, - "created_by": { - "type": "integer", - "readOnly": true, - "nullable": true + "status_url": { + "type": "string" }, - "created_by_username": { + "failure_code": { "type": "string", - "readOnly": true + "description": "Superuser only: closed operational failure code." }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "first_audit_log_id": { + "type": "integer", + "nullable": true, + "description": "Superuser only diagnostic." }, - "updated_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "first_batch_seq": { + "type": "integer", + "nullable": true, + "description": "Superuser only diagnostic." } }, "required": [ + "coverage", "created_at", - "created_by", - "created_by_username", + "filters", "id", - "kind", - "machine_type_name", "makerspace", - "moved_to_inventory_at", - "name", - "ordered_at", - "purchaser", - "purchaser_username", - "receipts", - "received_at", - "resulting_machine", - "resulting_pool", - "resulting_product", - "source_pool", - "source_product", + "snapshot_max_id", + "state", + "status_url", + "terminal", "updated_at" ] }, - "ToBuyItemKindEnum": { - "enum": [ - "hardware", - "printing" - ], - "type": "string", - "description": "* `hardware` - Hardware\n* `printing` - Printing" - }, - "ToBuyItemStatusEnum": { - "enum": [ - "requested", - "approved", - "ordered", - "received", - "cancelled" - ], - "type": "string", - "description": "* `requested` - Requested\n* `approved` - Approved\n* `ordered` - Ordered\n* `received` - Received\n* `cancelled` - Cancelled" - }, - "ToBuyMachineType": { + "VerificationRunPage": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "count": { + "type": "integer" }, - "name": { + "next": { "type": "string", - "readOnly": true - } - }, - "required": [ - "id", - "name" - ] - }, - "ToBuyMachineTypeOptions": { - "type": "object", - "description": "Options plus a SERVER-derived answer to \"must the caller pick one?\".\n\nThe console must not infer this from effective actions. A null-`assigned_role`\nlegacy membership is scope-exempt and cannot be expressed that way, and \"holds\nMANAGE_MACHINES\" is not the same question as \"is narrowed by machine scope\" -- a role\nholding MANAGE_MAKERSPACE holds both and is exempt. Deriving it here keeps one answer\non the server, the same reason the dashboard carries `scope_mode`.", - "properties": { - "machine_type_required": { - "type": "boolean" + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true }, "results": { "type": "array", "items": { - "$ref": "#/components/schemas/ToBuyMachineType" + "$ref": "#/components/schemas/VerificationRun" } } }, "required": [ - "machine_type_required", + "count", + "next", + "previous", "results" ] }, - "ToBuyReceipt": { + "VerificationRunStateEnum": { + "enum": [ + "QUEUED", + "RUNNING", + "RETRYING", + "SUCCEEDED", + "INCOMPLETE", + "FAILED", + "TIMED_OUT" + ], + "type": "string", + "description": "* `QUEUED` - Queued\n* `RUNNING` - Running\n* `RETRYING` - Retrying\n* `SUCCEEDED` - Succeeded\n* `INCOMPLETE` - Incomplete\n* `FAILED` - Failed\n* `TIMED_OUT` - Timed out" + }, + "VersionEnum": { + "enum": [ + 1 + ], + "type": "integer", + "description": "* `1` - 1" + }, + "Visibility68aEnum": { + "enum": [ + "draft", + "published" + ], + "type": "string", + "description": "* `draft` - Draft\n* `published` - Published" + }, + "VisibilityE49Enum": { + "enum": [ + "draft", + "published" + ], + "type": "string", + "description": "* `draft` - draft\n* `published` - published" + }, + "Vouch": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true + "voucher_id": { + "type": "integer" + }, + "voucher_name": { + "type": "string" }, "created_at": { "type": "string", "format": "date-time", "readOnly": true }, - "uploaded_by": { - "type": "integer", - "readOnly": true, - "nullable": true - }, - "uploaded_by_username": { + "expires_at": { "type": "string", - "readOnly": true + "format": "date-time" } }, "required": [ "created_at", - "id", - "uploaded_by", - "uploaded_by_username" - ] - }, - "ToBuyReceiptFinalize": { - "type": "object", - "properties": { - "object_key": { - "type": "string", - "maxLength": 512 - } - }, - "required": [ - "object_key" + "expires_at", + "voucher_id", + "voucher_name" ] }, - "ToBuyReceiptPresign": { + "WaiverAcceptResponse": { "type": "object", "properties": { - "filename": { - "type": "string", - "maxLength": 255 + "accepted": { + "type": "boolean" }, - "content_type": { + "version": { "type": "string", - "maxLength": 100 + "nullable": true } }, "required": [ - "content_type", - "filename" + "accepted", + "version" ] }, - "ToBuyReceiptUploadResponse": { + "WaiverPublish": { "type": "object", "properties": { - "object_key": { + "body": { "type": "string" }, - "upload": { - "type": "object", - "additionalProperties": {} - } - }, - "required": [ - "object_key", - "upload" - ] - }, - "ToBuyReceiptUrl": { - "type": "object", - "properties": { - "url": { + "version": { "type": "string", - "format": "uri" - } - }, - "required": [ - "url" - ] - }, - "TopBorrowersReport": { - "type": "object", - "properties": { - "rows": { - "type": "array", - "items": { - "type": "array", - "items": {} - } + "maxLength": 64 }, - "typed_rows": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TopBorrowersReportRow" - } + "clear": { + "type": "boolean", + "default": false } - }, - "required": [ - "rows", - "typed_rows" - ] + } }, - "TopBorrowersReportRow": { + "WaiverStateEnum": { + "enum": [ + "not_required", + "current", + "missing" + ], + "type": "string", + "description": "* `not_required` - not_required\n* `current` - current\n* `missing` - missing" + }, + "WalkInAdmissionOutcome": { "type": "object", "properties": { - "makerspace_id": { + "user_id": { "type": "integer" }, - "holder": { + "display_name": { "type": "string" }, - "requests": { - "type": "integer" + "username": { + "type": "string" }, - "items_borrowed": { - "type": "integer" + "admission_request": { + "$ref": "#/components/schemas/AdmissionRequest" } }, "required": [ - "holder", - "items_borrowed", - "requests" + "admission_request", + "display_name", + "user_id", + "username" ] }, - "TrackingModeB86Enum": { - "enum": [ - "quantity", - "individual" - ], - "type": "string", - "description": "* `quantity` - Quantity\n* `individual` - Individual" - }, - "TypeEnum": { - "enum": [ - "box", - "asset", - "product" - ], - "type": "string", - "description": "* `box` - box\n* `asset` - asset\n* `product` - product" + "WalkInAdmissionResult": { + "oneOf": [ + { + "$ref": "#/components/schemas/DirectLoanMember" + }, + { + "$ref": "#/components/schemas/WalkInAdmissionOutcome" + } + ] }, - "TypedError": { + "WalkInMemberCreate": { "type": "object", "properties": { - "detail": { - "type": "string" + "display_name": { + "type": "string", + "maxLength": 200 }, - "code": { - "type": "string" + "email": { + "oneOf": [ + { + "type": "string", + "format": "email", + "default": "" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "phone": { + "type": "string", + "default": "", + "maxLength": 32 + }, + "application_answers": { + "nullable": true } }, "required": [ - "code", - "detail" + "display_name" ] }, - "TypedManualUsage": { + "Warranty": { "type": "object", "properties": { - "machine_id": { - "type": "integer", - "minimum": 1 - }, - "consumable_pool_id": { + "id": { "type": "integer", - "minimum": 1, - "nullable": true + "readOnly": true }, - "service_request_id": { - "type": "integer", - "minimum": 1, - "nullable": true + "host_kind": { + "type": "string", + "readOnly": true }, - "duration_minutes": { + "host_id": { "type": "integer", - "minimum": 0 + "readOnly": true }, - "outcome": { - "$ref": "#/components/schemas/TypedManualUsageOutcomeEnum" + "host_label": { + "type": "string", + "readOnly": true }, - "percent_complete": { + "asset_id": { "type": "integer", - "maximum": 100, - "minimum": 0, - "default": 100 - }, - "reason": { - "type": "string" - }, - "grams": { - "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", - "default": "0.00" + "nullable": true, + "readOnly": true }, - "quantity": { + "asset_tag": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$" - }, - "metering_unit": { - "$ref": "#/components/schemas/TypedManualUsageMeteringUnitEnum" + "nullable": true, + "readOnly": true }, - "note": { + "serial_number": { "type": "string", - "maxLength": 255 - } - }, - "required": [ - "duration_minutes", - "machine_id", - "outcome" - ] - }, - "TypedManualUsageMeteringUnitEnum": { - "enum": [ - "minutes", - "weight", - "volume", - "length", - "count" - ], - "type": "string", - "description": "* `minutes` - minutes\n* `weight` - weight\n* `volume` - volume\n* `length` - length\n* `count` - count" - }, - "TypedManualUsageOutcomeEnum": { - "enum": [ - "success", - "failed" - ], - "type": "string", - "description": "* `success` - success\n* `failed` - failed" - }, - "TypedManualUsageResponse": { - "type": "object", - "properties": { - "id": { - "type": "integer", + "nullable": true, "readOnly": true }, "machine_id": { "type": "integer", + "nullable": true, "readOnly": true }, - "consumable_pool_id": { - "type": "integer", + "machine_name": { + "type": "string", + "nullable": true, + "readOnly": true + }, + "purchased_on": { + "type": "string", + "format": "date", "readOnly": true, "nullable": true }, - "service_request_id": { - "type": "integer", + "warranty_expires_on": { + "type": "string", + "format": "date", "readOnly": true, "nullable": true }, - "duration_minutes": { - "type": "integer", - "readOnly": true - }, - "outcome": { + "vendor_name": { "type": "string", "readOnly": true }, - "percent_complete": { - "type": "integer", - "readOnly": true - }, - "reason": { + "vendor_contact": { "type": "string", "readOnly": true }, - "consumed_grams": { + "status": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "metering_unit": { - "allOf": [ - { - "$ref": "#/components/schemas/TypedManualUsageResponseMeteringUnitEnum" - } - ], + "documents": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WarrantyDocument" + }, + "readOnly": true + } + }, + "required": [ + "asset_id", + "asset_tag", + "documents", + "host_id", + "host_kind", + "host_label", + "id", + "machine_id", + "machine_name", + "purchased_on", + "serial_number", + "status", + "vendor_contact", + "vendor_name", + "warranty_expires_on" + ] + }, + "WarrantyDocument": { + "type": "object", + "properties": { + "id": { + "type": "integer", "readOnly": true }, - "consumed_quantity": { + "original_filename": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,10}(?:\\.\\d{0,2})?$", "readOnly": true }, - "hours": { + "content_type": { "type": "string", - "format": "decimal", - "pattern": "^-?\\d{0,8}(?:\\.\\d{0,2})?$", "readOnly": true }, - "note": { - "type": "string", + "size_bytes": { + "type": "integer", "readOnly": true }, "created_at": { @@ -105097,560 +128498,630 @@ } }, "required": [ - "consumable_pool_id", - "consumed_grams", - "consumed_quantity", + "content_type", "created_at", - "duration_minutes", - "hours", "id", - "machine_id", - "metering_unit", - "note", - "outcome", - "percent_complete", - "reason", - "service_request_id" + "original_filename", + "size_bytes" ] }, - "TypedManualUsageResponseMeteringUnitEnum": { - "enum": [ - "minutes", - "weight", - "volume", - "length", - "count" - ], - "type": "string", - "description": "* `minutes` - Minutes\n* `weight` - Weight\n* `volume` - Volume\n* `length` - Length\n* `count` - Count" - }, - "UnsubscribeResult": { + "WarrantyDocumentFinalize": { "type": "object", "properties": { - "detail": { - "type": "string" + "object_key": { + "type": "string", + "maxLength": 300 + }, + "original_filename": { + "type": "string", + "maxLength": 255 } }, "required": [ - "detail" + "object_key", + "original_filename" ] }, - "UsageResource": { + "WarrantyDocumentPresign": { "type": "object", "properties": { - "key": { - "type": "string" - }, - "label": { - "type": "string" - }, - "used": { - "type": "integer", - "nullable": true - }, - "limit": { - "type": "integer", - "nullable": true + "filename": { + "type": "string", + "maxLength": 255 }, - "unit": { - "$ref": "#/components/schemas/UsageResourceUnitEnum" + "content_type": { + "type": "string", + "maxLength": 100 } }, "required": [ - "key", - "label", - "limit", - "unit", - "used" + "content_type", + "filename" ] }, - "UsageResourceUnitEnum": { - "enum": [ - "bytes", - "count" - ], - "type": "string", - "description": "* `bytes` - bytes\n* `count` - count" - }, - "User": { + "WarrantyDocumentUploadResponse": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "username": { - "type": "string", - "description": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", - "pattern": "^[\\w.@+-]+$", - "maxLength": 150 - }, - "email": { - "title": "Email address", - "oneOf": [ - { - "type": "string", - "format": "email", - "maxLength": 254 - }, - { - "type": "string", - "maxLength": 0 - } - ] - }, - "first_name": { - "type": "string", - "maxLength": 150 - }, - "last_name": { - "type": "string", - "maxLength": 150 - }, - "role": { - "$ref": "#/components/schemas/UserRoleEnum" - }, - "access_status": { - "$ref": "#/components/schemas/AccessStatusEnum" - }, - "restriction_reason": { + "object_key": { "type": "string" }, - "telegram_user_id": { - "type": "string", - "maxLength": 64 - }, - "external_checkin_user_id": { + "upload": { + "type": "object", + "additionalProperties": {} + } + }, + "required": [ + "object_key", + "upload" + ] + }, + "WarrantyDocumentUrl": { + "type": "object", + "properties": { + "url": { "type": "string", - "maxLength": 128 - }, - "is_active": { - "type": "boolean", - "title": "Active", - "description": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts." + "format": "uri" } }, "required": [ - "id", - "username" + "url" ] }, - "UserRoleEnum": { - "enum": [ - "superadmin", - "space_manager", - "requester" - ], - "type": "string", - "description": "* `superadmin` - Super Admin\n* `space_manager` - Space Manager\n* `requester` - Requester" - }, - "VerificationAccepted": { + "WarrantyExpiryReport": { "type": "object", "properties": { - "id": { - "type": "integer" - }, - "state": { - "type": "string" - }, - "deduplicated": { - "type": "boolean" + "rows": { + "type": "array", + "items": { + "type": "array", + "items": {} + } }, - "status_url": { - "type": "string" + "typed_rows": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WarrantyExpiryRow" + } } }, "required": [ - "deduplicated", - "id", - "state", - "status_url" + "rows", + "typed_rows" ] }, - "VerificationCoverage": { + "WarrantyExpiryRow": { "type": "object", "properties": { - "attempt": { - "type": "integer" - }, - "chain_status": { - "$ref": "#/components/schemas/ChainStatusEnum" - }, - "anchor_status": { - "$ref": "#/components/schemas/AnchorStatusEnum" - }, - "row_count": { - "type": "integer" - }, - "mac_valid_count": { - "type": "integer" - }, - "unattested_count": { + "makerspace_id": { "type": "integer" }, - "mac_invalid_count": { + "warranty_id": { "type": "integer" }, - "mac_unavailable_count": { + "machine_id": { "type": "integer" }, - "unbatched_count": { - "type": "integer" + "machine_name": { + "type": "string" }, - "local_batch_verified_count": { - "type": "integer" + "machine_type": { + "type": "string" }, - "batch_invalid_count": { - "type": "integer" + "purchased_on": { + "type": "string", + "format": "date", + "nullable": true }, - "batch_unavailable_count": { - "type": "integer" + "warranty_expires_on": { + "type": "string", + "format": "date" }, - "externally_anchored_count": { + "days_until_expiry": { "type": "integer" } }, "required": [ - "anchor_status", - "attempt", - "batch_invalid_count", - "batch_unavailable_count", - "chain_status", - "externally_anchored_count", - "local_batch_verified_count", - "mac_invalid_count", - "mac_unavailable_count", - "mac_valid_count", - "row_count", - "unattested_count", - "unbatched_count" + "days_until_expiry", + "machine_id", + "machine_name", + "machine_type", + "purchased_on", + "warranty_expires_on", + "warranty_id" ] }, - "VerificationFilters": { + "WarrantyReportRow": { "type": "object", "properties": { - "action": { + "host_kind": { "type": "string" }, - "target_type": { - "type": "string" + "host_id": { + "type": "integer" }, - "target_id": { + "host_label": { "type": "string" }, - "actor": { - "type": "integer", + "serial_number": { + "type": "string", "nullable": true }, - "start": { + "vendor_name": { "type": "string", - "format": "date-time", "nullable": true }, - "end": { + "purchased_on": { "type": "string", - "format": "date-time", + "format": "date", "nullable": true - } - }, - "required": [ - "action", - "actor", - "end", - "start", - "target_id", - "target_type" - ] - }, - "VerificationReport": { - "type": "object", - "properties": { - "format_version": { - "type": "integer" - }, - "target_makerspace_id": { - "type": "integer" - }, - "imported": { - "type": "object", - "additionalProperties": {} - }, - "resolved": { - "type": "object", - "additionalProperties": {} - }, - "dropped": { - "type": "object", - "additionalProperties": {} }, - "identities_linked": { - "type": "integer" + "warranty_expires_on": { + "type": "string", + "format": "date", + "nullable": true }, - "identities_created": { - "type": "integer" + "status": { + "type": "string" }, - "external_references_created": { + "document_count": { "type": "integer" } }, "required": [ - "dropped", - "external_references_created", - "format_version", - "identities_created", - "identities_linked", - "imported", - "resolved", - "target_makerspace_id" + "document_count", + "host_id", + "host_kind", + "host_label", + "purchased_on", + "serial_number", + "status", + "vendor_name", + "warranty_expires_on" ] }, - "VerificationRequest": { + "WarrantyUpsert": { "type": "object", - "description": "The audit-list filters, minus ``makerspace`` (it comes from the path).", "properties": { - "action": { - "type": "string", - "maxLength": 128 - }, - "target_type": { + "purchased_on": { "type": "string", - "maxLength": 200 + "format": "date", + "nullable": true }, - "target_id": { + "warranty_expires_on": { "type": "string", - "maxLength": 100 - }, - "actor": { - "type": "integer", - "minimum": 1 + "format": "date", + "nullable": true }, - "start": { + "vendor_name": { "type": "string", - "format": "date-time" + "maxLength": 200 }, - "end": { + "vendor_contact": { "type": "string", - "format": "date-time" + "maxLength": 200 } } }, - "VerificationRun": { + "WatchResponse": { "type": "object", "properties": { - "id": { - "type": "integer" + "subscribed": { + "type": "boolean" + } + }, + "required": [ + "subscribed" + ] + }, + "WebhookResponse": { + "type": "object", + "description": "Every provider webhook answers `{\"detail\": ...}`, acknowledged or refused.", + "properties": { + "detail": { + "type": "string" + } + }, + "required": [ + "detail" + ] + }, + "Website": { + "type": "object", + "properties": { + "tagline": { + "type": "string", + "maxLength": 300 }, - "makerspace": { - "type": "integer" + "accent_color": { + "oneOf": [ + { + "type": "string", + "pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "state": { - "$ref": "#/components/schemas/VerificationRunStateEnum" + "section_order": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SectionOrderEnum" + }, + "maxItems": 9, + "minItems": 9 }, - "terminal": { - "type": "boolean" + "section_enabled": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } }, - "snapshot_max_id": { + "venue": { + "allOf": [ + { + "$ref": "#/components/schemas/WebsiteVenue" + } + ], + "nullable": true + }, + "revision": { "type": "integer" }, - "filters": { - "$ref": "#/components/schemas/VerificationFilters" + "released": { + "type": "boolean" }, - "coverage": { - "$ref": "#/components/schemas/VerificationCoverage" + "inherited": { + "type": "boolean" }, - "created_at": { - "type": "string", - "format": "date-time" + "sections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsiteSection" + } }, - "updated_at": { - "type": "string", - "format": "date-time" + "sessions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsiteSession" + } }, - "status_url": { + "assets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsiteAsset" + } + } + }, + "required": [ + "inherited", + "released", + "revision", + "sections", + "sessions" + ] + }, + "WebsiteAsset": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "alt_text": { "type": "string" }, - "failure_code": { + "size_bytes": { + "type": "integer" + } + }, + "required": [ + "alt_text", + "id", + "size_bytes" + ] + }, + "WebsiteAssetInput": { + "type": "object", + "properties": { + "file": { "type": "string", - "description": "Superuser only: closed operational failure code." + "format": "uri" }, - "first_audit_log_id": { - "type": "integer", - "nullable": true, - "description": "Superuser only diagnostic." + "alt_text": { + "type": "string", + "maxLength": 200 }, - "first_batch_seq": { + "replace_id": { "type": "integer", - "nullable": true, - "description": "Superuser only diagnostic." + "minimum": 1 } }, "required": [ - "coverage", - "created_at", - "filters", - "id", - "makerspace", - "snapshot_max_id", - "state", - "status_url", - "terminal", - "updated_at" + "alt_text", + "file" ] }, - "VerificationRunPage": { + "WebsiteContentInput": { "type": "object", "properties": { - "count": { - "type": "integer" - }, - "next": { + "tagline": { "type": "string", - "format": "uri", - "nullable": true + "maxLength": 300 }, - "previous": { - "type": "string", - "format": "uri", + "accent_color": { + "oneOf": [ + { + "type": "string", + "pattern": "^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "section_order": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SectionOrderEnum" + }, + "maxItems": 9, + "minItems": 9 + }, + "section_enabled": { + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, + "venue": { + "allOf": [ + { + "$ref": "#/components/schemas/WebsiteVenue" + } + ], "nullable": true }, - "results": { + "revision": { + "type": "integer", + "minimum": 0 + }, + "sections": { "type": "array", "items": { - "$ref": "#/components/schemas/VerificationRun" + "$ref": "#/components/schemas/WebsiteSectionInput" + } + }, + "sessions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WebsiteSessionInput" } } }, "required": [ - "count", - "next", - "previous", - "results" + "revision", + "sections", + "sessions" ] }, - "VerificationRunStateEnum": { - "enum": [ - "QUEUED", - "RUNNING", - "RETRYING", - "SUCCEEDED", - "INCOMPLETE", - "FAILED", - "TIMED_OUT" - ], - "type": "string", - "description": "* `QUEUED` - Queued\n* `RUNNING` - Running\n* `RETRYING` - Retrying\n* `SUCCEEDED` - Succeeded\n* `INCOMPLETE` - Incomplete\n* `FAILED` - Failed\n* `TIMED_OUT` - Timed out" + "WebsiteDelayInput": { + "type": "object", + "properties": { + "delay_minutes": { + "type": "integer", + "maximum": 1440, + "minimum": 0 + } + }, + "required": [ + "delay_minutes" + ] }, - "VersionEnum": { - "enum": [ - 1 - ], - "type": "integer", - "description": "* `1` - 1" + "WebsiteOrigins": { + "type": "object", + "properties": { + "origins": { + "type": "array", + "items": { + "type": "string", + "maxLength": 255 + }, + "maxItems": 20 + } + }, + "required": [ + "origins" + ] }, - "Vouch": { + "WebsiteReleaseInput": { "type": "object", "properties": { - "voucher_id": { - "type": "integer" - }, - "voucher_name": { - "type": "string" - }, - "created_at": { - "type": "string", - "format": "date-time", - "readOnly": true + "released": { + "type": "boolean" }, - "expires_at": { - "type": "string", - "format": "date-time" + "revision": { + "type": "integer", + "minimum": 1 } }, "required": [ - "created_at", - "expires_at", - "voucher_id", - "voucher_name" + "released", + "revision" ] }, - "WaiverAcceptResponse": { + "WebsiteSection": { "type": "object", "properties": { - "accepted": { - "type": "boolean" + "kind": { + "$ref": "#/components/schemas/Kind21fEnum" + }, + "slug": { + "type": "string", + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "title": { + "type": "string", + "maxLength": 200 + }, + "body": { + "type": "string", + "default": "", + "maxLength": 10000 + }, + "url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "default": "", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "headline": { + "type": "string", + "maxLength": 200 + }, + "organization": { + "type": "string", + "maxLength": 200 + }, + "website_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "linkedin_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "x_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "version": { + "sponsor_tier": { + "$ref": "#/components/schemas/SponsorTierEnum" + }, + "icon": { + "type": "string", + "maxLength": 40 + }, + "asset_id": { + "type": "integer", + "minimum": 1, + "nullable": true + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/Visibility68aEnum" + } + ], + "default": "draft" + }, + "position": { + "type": "integer", + "maximum": 1000, + "minimum": 0, + "default": 0 + }, + "image_url": { "type": "string", "nullable": true + }, + "image_alt_text": { + "type": "string" } }, "required": [ - "accepted", - "version" + "image_alt_text", + "image_url", + "kind", + "slug", + "title" ] }, - "WaiverPublish": { + "WebsiteSectionInput": { "type": "object", "properties": { - "body": { - "type": "string" + "kind": { + "$ref": "#/components/schemas/Kind21fEnum" }, - "version": { + "slug": { "type": "string", - "maxLength": 64 + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "clear": { - "type": "boolean", - "default": false - } - } - }, - "WalkInAdmissionOutcome": { - "type": "object", - "properties": { - "user_id": { - "type": "integer" + "title": { + "type": "string", + "maxLength": 200 }, - "display_name": { - "type": "string" + "body": { + "type": "string", + "default": "", + "maxLength": 10000 }, - "username": { - "type": "string" + "url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "default": "", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] }, - "admission_request": { - "$ref": "#/components/schemas/AdmissionRequest" - } - }, - "required": [ - "admission_request", - "display_name", - "user_id", - "username" - ] - }, - "WalkInAdmissionResult": { - "oneOf": [ - { - "$ref": "#/components/schemas/DirectLoanMember" + "headline": { + "type": "string", + "maxLength": 200 }, - { - "$ref": "#/components/schemas/WalkInAdmissionOutcome" - } - ] - }, - "WalkInMemberCreate": { - "type": "object", - "properties": { - "display_name": { + "organization": { "type": "string", "maxLength": 200 }, - "email": { + "website_url": { "oneOf": [ { "type": "string", - "format": "email", - "default": "" + "format": "uri", + "maxLength": 500 }, { "type": "string", @@ -105658,301 +129129,357 @@ } ] }, - "phone": { + "linkedin_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "x_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "sponsor_tier": { + "$ref": "#/components/schemas/SponsorTierEnum" + }, + "icon": { "type": "string", - "default": "", - "maxLength": 32 + "maxLength": 40 }, - "application_answers": { + "asset_id": { + "type": "integer", + "minimum": 1, "nullable": true + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/Visibility68aEnum" + } + ], + "default": "draft" + }, + "position": { + "type": "integer", + "maximum": 1000, + "minimum": 0, + "default": 0 } }, "required": [ - "display_name" + "kind", + "slug", + "title" ] }, - "Warranty": { + "WebsiteSession": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "host_kind": { + "slug": { "type": "string", - "readOnly": true - }, - "host_id": { - "type": "integer", - "readOnly": true + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "host_label": { + "title": { "type": "string", - "readOnly": true + "maxLength": 200 }, - "asset_id": { - "type": "integer", - "nullable": true, - "readOnly": true + "body": { + "type": "string", + "default": "", + "maxLength": 10000 }, - "asset_tag": { + "starts_at": { "type": "string", - "nullable": true, - "readOnly": true + "format": "date-time" }, - "serial_number": { + "ends_at": { "type": "string", - "nullable": true, - "readOnly": true + "format": "date-time" }, - "machine_id": { - "type": "integer", + "track": { + "type": "string", "nullable": true, - "readOnly": true + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "machine_name": { + "room": { "type": "string", "nullable": true, - "readOnly": true + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "purchased_on": { - "type": "string", - "format": "date", - "readOnly": true, - "nullable": true + "speakers": { + "type": "array", + "items": { + "type": "string", + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "maxItems": 20 }, - "warranty_expires_on": { - "type": "string", - "format": "date", - "readOnly": true, - "nullable": true + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/Visibility68aEnum" + } + ], + "default": "draft" }, - "vendor_name": { - "type": "string", - "readOnly": true + "delay_minutes": { + "type": "integer" }, - "vendor_contact": { + "effective_starts_at": { "type": "string", - "readOnly": true + "format": "date-time" }, - "status": { + "effective_ends_at": { "type": "string", - "readOnly": true - }, - "documents": { - "type": "array", - "items": { - "$ref": "#/components/schemas/WarrantyDocument" - }, - "readOnly": true + "format": "date-time" } }, "required": [ - "asset_id", - "asset_tag", - "documents", - "host_id", - "host_kind", - "host_label", - "id", - "machine_id", - "machine_name", - "purchased_on", - "serial_number", - "status", - "vendor_contact", - "vendor_name", - "warranty_expires_on" + "delay_minutes", + "effective_ends_at", + "effective_starts_at", + "ends_at", + "slug", + "starts_at", + "title" ] }, - "WarrantyDocument": { + "WebsiteSessionInput": { "type": "object", "properties": { - "id": { - "type": "integer", - "readOnly": true - }, - "original_filename": { + "slug": { "type": "string", - "readOnly": true + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "content_type": { + "title": { "type": "string", - "readOnly": true - }, - "size_bytes": { - "type": "integer", - "readOnly": true + "maxLength": 200 }, - "created_at": { + "body": { "type": "string", - "format": "date-time", - "readOnly": true - } - }, - "required": [ - "content_type", - "created_at", - "id", - "original_filename", - "size_bytes" - ] - }, - "WarrantyDocumentFinalize": { - "type": "object", - "properties": { - "object_key": { + "default": "", + "maxLength": 10000 + }, + "starts_at": { "type": "string", - "maxLength": 300 + "format": "date-time" }, - "original_filename": { + "ends_at": { "type": "string", - "maxLength": 255 - } - }, - "required": [ - "object_key", - "original_filename" - ] - }, - "WarrantyDocumentPresign": { - "type": "object", - "properties": { - "filename": { + "format": "date-time" + }, + "track": { "type": "string", - "maxLength": 255 + "nullable": true, + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "content_type": { + "room": { "type": "string", - "maxLength": 100 - } - }, - "required": [ - "content_type", - "filename" - ] - }, - "WarrantyDocumentUploadResponse": { - "type": "object", - "properties": { - "object_key": { - "type": "string" + "nullable": true, + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" }, - "upload": { - "type": "object", - "additionalProperties": {} + "speakers": { + "type": "array", + "items": { + "type": "string", + "maxLength": 80, + "pattern": "^[-a-zA-Z0-9_]+$" + }, + "maxItems": 20 + }, + "visibility": { + "allOf": [ + { + "$ref": "#/components/schemas/Visibility68aEnum" + } + ], + "default": "draft" } }, "required": [ - "object_key", - "upload" + "ends_at", + "slug", + "starts_at", + "title" ] }, - "WarrantyDocumentUrl": { + "WebsiteVenue": { "type": "object", "properties": { - "url": { + "name": { "type": "string", - "format": "uri" - } - }, - "required": [ - "url" - ] - }, - "WarrantyReportRow": { - "type": "object", - "properties": { - "host_kind": { - "type": "string" + "default": "", + "maxLength": 200 }, - "host_id": { - "type": "integer" + "address": { + "type": "string", + "default": "", + "maxLength": 500 }, - "host_label": { - "type": "string" + "city": { + "type": "string", + "default": "", + "maxLength": 200 }, - "serial_number": { + "country": { "type": "string", + "default": "", + "maxLength": 200 + }, + "latitude": { + "type": "number", + "format": "double", + "maximum": 90, + "minimum": -90, "nullable": true }, - "vendor_name": { - "type": "string", + "longitude": { + "type": "number", + "format": "double", + "maximum": 180, + "minimum": -180, "nullable": true }, - "purchased_on": { + "map_url": { + "oneOf": [ + { + "type": "string", + "format": "uri", + "default": "", + "maxLength": 500 + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, + "directions": { "type": "string", - "format": "date", - "nullable": true + "default": "", + "maxLength": 2000 }, - "warranty_expires_on": { + "parking": { "type": "string", - "format": "date", - "nullable": true + "default": "", + "maxLength": 2000 }, - "status": { - "type": "string" + "public_transport": { + "type": "string", + "default": "", + "maxLength": 2000 }, - "document_count": { - "type": "integer" + "accessibility_notes": { + "type": "string", + "default": "", + "maxLength": 2000 } - }, - "required": [ - "document_count", - "host_id", - "host_kind", - "host_label", - "purchased_on", - "serial_number", - "status", - "vendor_name", - "warranty_expires_on" - ] + } }, - "WarrantyUpsert": { + "WhatsAppBinding": { "type": "object", "properties": { - "purchased_on": { + "id": { + "type": "integer", + "readOnly": true + }, + "feature": { "type": "string", - "format": "date", - "nullable": true + "maxLength": 32 }, - "warranty_expires_on": { + "event": { "type": "string", - "format": "date", - "nullable": true + "maxLength": 64 }, - "vendor_name": { + "audience": { + "$ref": "#/components/schemas/Audience356Enum" + }, + "business_account_id": { "type": "string", - "maxLength": 200 + "readOnly": true }, - "vendor_contact": { + "template_name": { "type": "string", - "maxLength": 200 - } - } - }, - "WatchResponse": { - "type": "object", - "properties": { - "subscribed": { + "maxLength": 512 + }, + "language": { + "type": "string", + "maxLength": 16 + }, + "parameters": {}, + "is_active": { "type": "boolean" + }, + "needs_review": { + "type": "boolean", + "readOnly": true + }, + "components": { + "writeOnly": true } }, "required": [ - "subscribed" + "audience", + "business_account_id", + "event", + "feature", + "id", + "language", + "needs_review", + "template_name" ] }, - "WebhookResponse": { + "WhatsAppBindingPage": { "type": "object", - "description": "Every provider webhook answers `{\"detail\": ...}`, acknowledged or refused.", "properties": { - "detail": { - "type": "string" + "count": { + "type": "integer" + }, + "next": { + "type": "string", + "format": "uri", + "nullable": true + }, + "previous": { + "type": "string", + "format": "uri", + "nullable": true + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WhatsAppBinding" + } } }, "required": [ - "detail" + "count", + "next", + "previous", + "results" ] }, "WitnessWaiverResponse": { @@ -106037,10 +129564,17 @@ "name": "sw_kiosk_station", "description": "Signed, Station/version/session-bound kiosk cookie. Session-bound bearer material, not device-bound." }, + "ServerHMAC": { + "type": "apiKey", + "in": "header", + "name": "X-Signature", + "description": "Tenant-bound server HMAC over METHOD, full path, timestamp, nonce (when present), body. X-Client-Id and X-Timestamp also required; see API client protocol." + }, "jwtAuth": { "type": "http", "scheme": "bearer", - "bearerFormat": "JWT" + "bearerFormat": "JWT", + "description": "Native staff sessions require a live device grant with staff authority. On non-member APIs, X-Makerspace-Id is mandatory and must select an active, servable local membership matching all URL/body targets. Member APIs may omit the header. Personal notification inbox, preferences and announcements accept member sessions; broadcasts and the makerspace-wide notification feed require staff authority." } } }, diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a7892e6..4fe940c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -19,11 +19,35 @@ android { manifestPlaceholders["deepLinkVerify"] = ((brand["server_url"] as? String)?.isNotBlank()==true).toString() minSdk = 26 targetSdk = 36 - versionCode = 1 - versionName = "0.1.0" + // Store uploads inject a fresh versionCode/versionName without editing source. + versionCode = (findProperty("spaceworks.versionCode") as String?)?.toInt() ?: 1 + versionName = (findProperty("spaceworks.versionName") as String?) ?: "0.1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" resValue("string", "app_name", brand["app_name"] as String) buildConfigField("String", "SERVER_URL", "\"${brand["server_url"] ?: ""}\"") + // Public Play Integrity Cloud project number (owner build input); 0 keeps production attestation unavailable. + val integrityProject = (findProperty("spaceworks.playIntegrityProject") as String?)?.toLongOrNull() + ?: (brand["play_integrity_cloud_project_number"] as? Number)?.toLong() ?: 0L + require(integrityProject >= 0) { "play_integrity_cloud_project_number must be a positive project number or null" } + buildConfigField("long", "PLAY_INTEGRITY_PROJECT", "${integrityProject}L") + } + // The owner's upload key is injected only in the private release environment; never committed. + val uploadKeystore = providers.environmentVariable("SPACEWORKS_UPLOAD_KEYSTORE").orNull + signingConfigs { + if (uploadKeystore != null) create("upload") { + storeFile = file(uploadKeystore) + storePassword = providers.environmentVariable("SPACEWORKS_UPLOAD_KEYSTORE_PASSWORD").get() + keyAlias = providers.environmentVariable("SPACEWORKS_UPLOAD_KEY_ALIAS").get() + keyPassword = providers.environmentVariable("SPACEWORKS_UPLOAD_KEY_PASSWORD").get() + } + } + buildTypes { + release { + isMinifyEnabled = true + isShrinkResources = true + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + signingConfig = signingConfigs.findByName("upload") + } } buildFeatures { compose = true; buildConfig = true } compileOptions { sourceCompatibility = JavaVersion.VERSION_17; targetCompatibility = JavaVersion.VERSION_17 } @@ -113,7 +137,12 @@ openApiGenerate { // generator7.26 inline-union scalar serializer defects without editing schema. schemaMappings.set(mapOf( "TenantBootstrapMakerspace_map_url" to "String", + "SocialLogin_redirect_uri" to "String", + "ReauthenticationSocialStart_redirect_uri" to "String", + "WebsiteSection_url" to "String", + "WebsiteSection_website_url" to "String", "TenantBootstrapMakerspace_opening_hours" to "kotlinx.serialization.json.JsonElement", + "NullEnum" to "kotlinx.serialization.json.JsonElement", "TenantBootstrapMakerspace_opening_hours_oneOf_weekly" to "kotlinx.serialization.json.JsonElement" )) enumNameMappings.set(mapOf("name" to "nameAscending", "" to "none")) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..835cde9 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,35 @@ +# Release shrinking rules. Library consumer rules (Retrofit, OkHttp, kotlinx.serialization, Stripe, +# CameraX, ML Kit, Play Integrity, Firebase) are applied automatically; only gaps are listed here. + +# Generated Retrofit interfaces are resolved reflectively through their annotations and generic +# suspend signatures (Response); keep the metadata R8 would otherwise strip. +-keepattributes Signature, InnerClasses, EnclosingMethod, RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations, AnnotationDefault +-keep,allowobfuscation interface org.spaceworks.generated.api.** + +# kotlinx.serialization: keep generated serializers and companions of the generated client models. +-keepclassmembers @kotlinx.serialization.Serializable class org.spaceworks.generated.model.** { + *** Companion; + *** INSTANCE; + kotlinx.serialization.KSerializer serializer(...); +} +-keep class org.spaceworks.generated.model.**$$serializer { *; } +-keep class org.spaceworks.generated.model.**Serializer { *; } + +# Credential Manager loads its Play Services provider reflectively. +-if class androidx.credentials.CredentialManager +-keep class androidx.credentials.playservices.** { *; } + +# Razorpay Checkout (vendor-documented rules). +-keepclassmembers class * { @android.webkit.JavascriptInterface ; } +-keepattributes JavascriptInterface +-keepattributes *Annotation* +-dontwarn com.razorpay.** +-keep class com.razorpay.** { *; } +-optimizations !method/inlining/* +-keepclasseswithmembers class * { public void onPayment*(...); } + +# Optional Google Play services/annotation references that are not packaged. +-dontwarn com.google.errorprone.annotations.** +-dontwarn javax.annotation.** +-dontwarn org.slf4j.impl.** +-dontwarn proguard.annotation.** diff --git a/app/src/androidTest/java/org/spaceworks/app/InsightsJourneyTest.kt b/app/src/androidTest/java/org/spaceworks/app/InsightsJourneyTest.kt new file mode 100644 index 0000000..a50a6a9 --- /dev/null +++ b/app/src/androidTest/java/org/spaceworks/app/InsightsJourneyTest.kt @@ -0,0 +1,89 @@ +package org.spaceworks.app + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Surface +import androidx.compose.ui.Modifier +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.unit.dp +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.spaceworks.app.auth.* +import org.spaceworks.app.insights.* +import org.spaceworks.app.member.MemberScope +import org.spaceworks.app.network.* +import org.spaceworks.app.theme.SpaceWorksTheme +import org.spaceworks.app.ui.AccountFlowScreen +import org.spaceworks.generated.model.* + +private class JourneyInsights:InsightsGateway { + var sms=ConsentChannel(true,false,true,"•••• 3210",false) + var whatsapp=ConsentChannel(true,false,false,"",false) + var writes=mutableListOf>() + private fun series(vararg points:Pair)=UsageSeries(points.map {UsagePoint(it.first,it.second.toString(),it.second.toDouble())},"","") + override suspend fun usage(scope:MemberScope)=UsageSummary(true,LoanUsage(12,10,2,series("Sep 2026" to 4,"Oct 2026" to 3)),null, + CountUsage(5,series("Oct 2026" to 2)),3,PaymentUsage(mapOf("inr" to "1250.00"),emptyMap())) + override suspend fun dashboard(scope:MemberScope)=StaffDashboard(StaffScope.HANDOVER,emptyList(), + listOf(StaffCounter("ready_to_issue",4),StaffCounter("due_returns",1)),listOf(StaffWidget("hidden","Analytics","count","9")),emptyMap()) + override suspend fun messaging(scope:MemberScope)=MessagingConsent(sms,whatsapp) + override suspend fun consent(scope:MemberScope,channel:String,optIn:Boolean):MessagingConsent { + writes+=channel to optIn;sms=sms.copy(consented=optIn);return MessagingConsent(sms,whatsapp) + } +} + +class InsightsJourneyTest { + @get:Rule val compose=createComposeRule() + private val journey by lazy {NativeJourney(compose,"v2")} + private fun show(content:@androidx.compose.runtime.Composable ()->Unit) { + DeviceWindowGuard.awaitClear() + compose.setContent {SpaceWorksTheme(DeviceProfile.dark) {Surface(Modifier.fillMaxSize()) {Box(Modifier.fillMaxSize().safeDrawingPadding()) {content()}}}} + } + private fun holder(api:InsightsGateway)=InsightsStateHolder(api).apply {activate(MemberScope(7,"fab-lab"))} + @Test fun memberUsageShowsServerNumbersWithWrittenValues() { + val insights=holder(JourneyInsights()) + show {Column(Modifier.verticalScroll(rememberScrollState()).padding(24.dp)) {MemberUsagePanel(insights)}} + journey.waitText("Your activity");journey.waitText("Out now") + journey.visible(compose.onNodeWithContentDescription("Sep 2026: 4",substring=true)) + journey.waitText("INR 1250.00");journey.shot("v2-member-usage") + } + @Test fun handoverStaffSeeQueueTilesWithoutAnalytics() { + val insights=holder(JourneyInsights()) + show {Column(Modifier.verticalScroll(rememberScrollState()).padding(24.dp)) {StaffDashboardPanel(insights)}} + journey.waitText("Handover queue");journey.waitText("Ready to issue");journey.waitText("Due back") + compose.onNodeWithText("Analytics").assertDoesNotExist();journey.shot("v2-staff-handover") + } + @Test fun consentSwitchFollowsServerAndExplainsVerification() { + val api=JourneyInsights();val insights=holder(api) + show {MessagingConsentScreen(insights,{},{})} + journey.waitText("Text and WhatsApp messages");journey.waitText("Messages go to •••• 3210.") + journey.waitText("Verify your phone number in Account and security before turning this on.") + compose.onNodeWithTag("consent-whatsapp").assertIsNotEnabled() + journey.clickTag("consent-sms") + compose.waitUntil(10000) {api.writes.isNotEmpty()} + assertEquals(listOf("sms" to true),api.writes);journey.waitText("On");journey.shot("v2-consent") + } + @Test fun phoneRestoreSendsCodeAndConfirmsRestoration() { + val auth=object:AuthGateway { + override suspend fun config()=PublicConfig(false,100) + override suspend fun login(username:String,password:String)=throw AssertionError() + override suspend fun enroll(challenge:String)=throw AssertionError() + override suspend fun verify(challenge:String,code:String,enrollment:Boolean)=throw AssertionError() + override suspend fun me()=throw AssertionError() + override suspend fun logout() {} + override suspend fun phoneStart(phone:String)=PhoneChallenge("c",300) + override suspend fun restorePhone(phone:String,code:String,newPassword:String,challenge:PhoneChallenge)=AccountRestoreResult(true,"Account restored.") + } + val accounts=AccountStateHolder(auth,{false}).apply {open(AccountRoute.RESTORE_PHONE)} + show {AccountFlowScreen(accounts,false)} + journey.waitText("Restore a deleted account");journey.shot("v2-restore-phone") + journey.replace(compose.onNodeWithTag("restorePhone"),"+919876543210");journey.clickTag("restoreSend") + journey.waitText("Enter the code we sent and choose a new password.") + journey.replace(compose.onNodeWithTag("restoreCode"),"123456") + journey.replace(compose.onNodeWithTag("restorePassword"),"new-password-1") + journey.replace(compose.onNodeWithTag("restoreConfirmation"),"new-password-1") + journey.clickTag("restoreSubmit");journey.waitText("Account restored");journey.shot("v2-restore-done") + } +} diff --git a/app/src/androidTest/java/org/spaceworks/app/NativeContractJourneyTest.kt b/app/src/androidTest/java/org/spaceworks/app/NativeContractJourneyTest.kt new file mode 100644 index 0000000..142e3d0 --- /dev/null +++ b/app/src/androidTest/java/org/spaceworks/app/NativeContractJourneyTest.kt @@ -0,0 +1,97 @@ +package org.spaceworks.app + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Surface +import androidx.compose.ui.Modifier +import androidx.compose.ui.test.* +import androidx.compose.ui.test.junit4.createComposeRule +import kotlinx.serialization.json.JsonPrimitive +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.spaceworks.app.account.* +import org.spaceworks.app.auth.* +import org.spaceworks.app.network.* +import org.spaceworks.app.theme.SpaceWorksTheme +import org.spaceworks.app.ui.AuthScreen +import org.spaceworks.generated.model.* + +/** Production auth/deletion UI against in-test gateways shaped like the generated native contract. */ +private class JourneyAuth(var minimum:String?=null):AuthGateway { + var codes=mutableListOf() + override val nativeSocialReady=true + override suspend fun config()=PublicConfig(false,100,native=NativeConfig(NativeMinVersion(minimum,null),NativeProviders(true,true,null,null,emptyList()))) + override suspend fun login(username:String,password:String):DeviceLoginResult=throw ApiFailure(401,"Wrong password.",null,null) + override suspend fun enroll(challenge:String)=throw AssertionError() + override suspend fun verify(challenge:String,code:String,enrollment:Boolean)=throw AssertionError() + override suspend fun me()=AuthUserPayload("member",1,"phone","","Phone Member","+919876543210",true,"member",false,false,emptyList()) + override suspend fun logout() {} + override suspend fun phoneStart(phone:String)=PhoneChallenge("journey-challenge",300) + override suspend fun phoneConfirm(phone:String,code:String,challenge:PhoneChallenge):PhoneLoginResult { + codes+=code + if(code!="123456")throw ApiFailure(400,"Invalid or expired code.",null,null) + val at=java.time.OffsetDateTime.parse("2026-10-17T00:00:00Z") + return PhoneLoginResult.DeviceTokenResponseValue(DeviceTokenResponse("a","r",emptyMap(), + DeviceGrant(java.util.UUID.fromString("00000000-0000-0000-0000-000000000001"),"android","org.spaceworks.app","development","active",at,at,at))) + } +} +private class JourneyDeletion:AccountDeletionGateway { + var blocked=true;var deleted=0 + override suspend fun preview()=if(blocked)DeletionPreview(false,true,"You can restore it within 90 days.", + listOf(DeletionBlocker("unpaid_charges",7,"fab-lab",null,2,"Two charges need payment."),DeletionBlocker("future_code",null,null,null,1,"A server-only review is open.")),true,listOf("password")) + else DeletionPreview(true,true,"You can restore it within 90 days.",emptyList(),true,listOf("password")) + override suspend fun password(password:String,code:String?)=if(password=="correct")DeletionProof.Verified(600) else throw ApiFailure(0,"x","reauth_failed",null) + override suspend fun startPhone()=throw AssertionError() + override suspend fun confirmPhone(token:PhoneProofToken,code:String)=throw AssertionError() + override suspend fun startSocial(provider:String)=throw AssertionError() + override suspend fun completeSocial(intent:SocialProofIntent,idToken:String)=throw AssertionError() + override suspend fun factor(token:String,code:String)=throw AssertionError() + override suspend fun delete():DeletionOutcome {deleted++;return DeletionOutcome.Deleted("2026-10-18T00:00:00Z","2027-01-16T00:00:00Z")} +} + +class NativeContractJourneyTest { + @get:Rule val compose=createComposeRule() + private val journey by lazy {NativeJourney(compose,"c-native")} + private fun show(content:@androidx.compose.runtime.Composable ()->Unit) { + DeviceWindowGuard.awaitClear() + compose.setContent {SpaceWorksTheme(DeviceProfile.dark) {Surface(Modifier.fillMaxSize()) {Box(Modifier.fillMaxSize().safeDrawingPadding()) {content()}}}} + } + @Test fun phoneSignInSendsCodeRejectsWrongCodeAndSignsIn() { + val api=JourneyAuth();val store=MemoryTokenStore();val holder=AuthStateHolder(api,store) + show {AuthScreen(AuthBundle(holder,store))} + journey.waitText("Continue with phone");journey.shot("c3-phone-entry") + journey.clickTag("phoneSignIn");journey.waitText("We'll text a six-digit code to this number.") + journey.replace(compose.onNodeWithTag("phoneNumber"),"9876543210");journey.clickTag("sendPhoneCode") + journey.waitText("Enter your verification code");journey.shot("c3-phone-code") + journey.replace(compose.onNodeWithTag("phoneCode"),"000000");journey.clickTag("verifyPhoneCode") + journey.waitText("Invalid or expired code.");journey.shot("c3-phone-invalid") + journey.replace(compose.onNodeWithTag("phoneCode"),"123456");journey.clickTag("verifyPhoneCode") + compose.waitUntil(10000) {holder.state.value.user!=null} + assertEquals(listOf("000000","123456"),api.codes);assertEquals(TokenPair("a","r"),store.read()) + } + @Test fun belowMinimumVersionShowsUpdateRequired() { + val api=JourneyAuth(minimum="999.0");val store=MemoryTokenStore();val holder=AuthStateHolder(api,store,appVersion=BuildConfig.VERSION_NAME) + show {AuthScreen(AuthBundle(holder,store))} + journey.waitText("Update required") + journey.visible(compose.onNodeWithText("Open Google Play"));journey.shot("c3-update-required") + compose.onNodeWithText("Email or username").assertDoesNotExist() + } + @Test fun deletionShowsBlockersThenReauthenticatesAndRequiresTypedConfirmation() { + val api=JourneyDeletion();val holder=AccountDeletionStateHolder(api) + show {AccountDeletionScreen(holder,{},{_,_->})} + journey.waitText("Resolve these before deleting") + journey.visible(compose.onNodeWithTag("deletion-blocker-unpaid_charges"));journey.waitText("A server-only review is open.") + journey.shot("c4-deletion-blockers") + api.blocked=false;journey.clickTag("deletion-recheck") + journey.waitText("Verify it is you") + journey.replace(compose.onNodeWithTag("deletion-password"),"wrong");journey.clickTag("deletion-authenticate") + journey.waitText("Verification failed. Check your password or code.");journey.shot("c4-deletion-proof-failed") + journey.replace(compose.onNodeWithTag("deletion-password"),"correct");journey.clickTag("deletion-authenticate") + journey.waitText("Confirm account deletion") + journey.replace(compose.onNodeWithTag("deletion-confirmation"),"delete") + compose.onNodeWithTag("deletion-submit").assertIsNotEnabled() + journey.replace(compose.onNodeWithTag("deletion-confirmation"),"DELETE");journey.shot("c4-deletion-confirm") + journey.clickTag("deletion-submit") + journey.waitText("Your account was deleted and this device is signed out.");assertEquals(1,api.deleted) + } +} diff --git a/app/src/androidTest/java/org/spaceworks/app/PaymentsJourneyTest.kt b/app/src/androidTest/java/org/spaceworks/app/PaymentsJourneyTest.kt index 9448d87..55b7a83 100644 --- a/app/src/androidTest/java/org/spaceworks/app/PaymentsJourneyTest.kt +++ b/app/src/androidTest/java/org/spaceworks/app/PaymentsJourneyTest.kt @@ -43,7 +43,7 @@ class PaymentsJourneyTest { @Composable fun paymentContent() { org.spaceworks.app.theme.SpaceWorksTheme(dark){ Surface(Modifier.fillMaxSize()){Box(Modifier.fillMaxSize().safeDrawingPadding()){ - PaymentsScreen(holder,true,{},launchStripe={launches.incrementAndGet();PaymentLaunchResult.DEMO_BLOCKED}) + PaymentsScreen(holder,true,{},launchCheckout={launches.incrementAndGet();PaymentLaunchResult.DEMO_BLOCKED}) }} } } diff --git a/app/src/debug/assets/staff/booking.json b/app/src/debug/assets/staff/booking.json index af295a0..7c341a5 100644 --- a/app/src/debug/assets/staff/booking.json +++ b/app/src/debug/assets/staff/booking.json @@ -11,5 +11,9 @@ "public_token": "71000000-0000-4000-8000-000000000001", "space_id": 901, "starts_at": "2026-10-16T09:00:00Z", - "status": "pending" + "status": "pending", + "arrived_at": null, + "cancelled_at": null, + "restore_until": null, + "status_before_cancel": "" } diff --git a/app/src/debug/assets/staff/event.json b/app/src/debug/assets/staff/event.json index f8506a2..885fe83 100644 --- a/app/src/debug/assets/staff/event.json +++ b/app/src/debug/assets/staff/event.json @@ -39,5 +39,14 @@ "ticketing_features": [], "timezone_name": "", "title": "Sample repair workshop", - "updated_at": "2026-10-16T09:00:00Z" + "updated_at": "2026-10-16T09:00:00Z", + "audience_label": null, + "badge_sensitive_fields_allowed": false, + "broadcasts_enabled": false, + "can_manage_staff": false, + "organization_admission_blocked": false, + "participating_organizations": [], + "report_features": [], + "show_registration_count": false, + "staff_permissions": [] } diff --git a/app/src/debug/assets/staff/registration.json b/app/src/debug/assets/staff/registration.json index 33e8432..5538317 100644 --- a/app/src/debug/assets/staff/registration.json +++ b/app/src/debug/assets/staff/registration.json @@ -10,5 +10,9 @@ "phone": "", "quoted_amount": null, "status": "registered", - "ticket_type_id": null + "ticket_type_id": null, + "cancelled_at": null, + "no_longer_eligible": false, + "restore_until": null, + "status_before_cancel": "" } diff --git a/app/src/debug/assets/staff/space.json b/app/src/debug/assets/staff/space.json index 8f55f3e..6a72f9e 100644 --- a/app/src/debug/assets/staff/space.json +++ b/app/src/debug/assets/staff/space.json @@ -27,5 +27,18 @@ "requester_notifications_enabled": null, "show_public_availability": false, "show_public_booker_names": false, - "updated_at": "2026-10-16T09:00:00Z" + "updated_at": "2026-10-16T09:00:00Z", + "broadcasts_enabled": false, + "can_copy_booking_rules": false, + "can_manage_grants": false, + "checkin_enabled": false, + "clone_enabled": false, + "embed_enabled": false, + "passes_enabled": false, + "recurring_enabled": false, + "report_features": [], + "staff_grants_enabled": false, + "staff_permissions": [], + "staff_tools_enabled": false, + "website_enabled": false } diff --git a/app/src/debug/java/org/spaceworks/app/account/DemoDeletionPreview.kt b/app/src/debug/java/org/spaceworks/app/account/DemoDeletionPreview.kt index a65eb8e..0532969 100644 --- a/app/src/debug/java/org/spaceworks/app/account/DemoDeletionPreview.kt +++ b/app/src/debug/java/org/spaceworks/app/account/DemoDeletionPreview.kt @@ -2,7 +2,7 @@ package org.spaceworks.app.account import androidx.compose.runtime.Composable -class DemoDeletionPreview(makerspaceIDs:List,private val makerName:(Int)->String?,private val canOpenStaff:(Int?)->Boolean) : AccountDeletionPreview { +class DemoDeletionPreview(makerspaceIDs:List,private val makerName:(Int)->String?,private val canOpenStaff:(Int?)->Boolean) : AccountDeletionFlow { private val holder=DemoAccountDeletion(makerspaceIDs) override fun clear(){holder.clear()} @Composable override fun Screen(onBack:()->Unit,onFix:(Int?,AccountFixDestination)->Unit,onDeleted:()->Unit) { diff --git a/app/src/debug/java/org/spaceworks/app/auth/DemoSupport.kt b/app/src/debug/java/org/spaceworks/app/auth/DemoSupport.kt index 355575c..866556e 100644 --- a/app/src/debug/java/org/spaceworks/app/auth/DemoSupport.kt +++ b/app/src/debug/java/org/spaceworks/app/auth/DemoSupport.kt @@ -35,15 +35,16 @@ object DemoSupport { staffApi?.authority?.allows(scope,action)==true } staff=staffApi?.let {org.spaceworks.app.staff.StaffStateHolder(it,staffAuthority,it.uploads,text=copyText)} - clearScope={runtime?.let(accountCoordinator::scopeChanged);staff?.clear();member.clear();actions.clear();payments.clear()} - clearPrivate={accounts?.clear();runtime?.let(accountCoordinator::sessionEnded);staff?.resetAccount();clearScope();links.clear();push.holder.pause()} + val insights=org.spaceworks.app.insights.InsightsStateHolder(org.spaceworks.app.insights.DemoInsightsGateway()) + clearScope={insights.clear();runtime?.let(accountCoordinator::scopeChanged);staff?.clear();member.clear();actions.clear();payments.clear()} + clearPrivate={insights.clear();accounts?.clear();runtime?.let(accountCoordinator::sessionEnded);staff?.resetAccount();clearScope();links.clear();push.holder.pause()} gateway.beforeSignOut={push.holder.disable(forgetConsent=false)} accounts=AccountStateHolder(gateway,{true},{holder.foreground()},text=copyText) runtime=NativeAccountRuntime("https://demo.invalid/",tokens,{holder.acceptedInstallationEpoch}, {holder.state.value.user?.id?.toString()}, {holder.state.value.selected?.let {org.spaceworks.app.member.MemberScope(it.id,it.slug)}}, holder::endLocalSession,holder::foreground,holder::dropConfirmedMembership) - val bundle=AuthBundle(holder,tokens,true,accounts,DemoAuthFlow(holder,text=copyText),member,actions,origin="https://demo.invalid/",push=push.holder,links=links,pushConfigured=true,sessionEpoch={tokens.snapshot().epoch},pushPreview=push.control,payments=payments,staff=staff, + val bundle=AuthBundle(holder,tokens,true,accounts,DemoAuthFlow(holder,text=copyText),member,actions,origin="https://demo.invalid/",push=push.holder,links=links,pushConfigured=true,sessionEpoch={tokens.snapshot().epoch},pushPreview=push.control,payments=payments,staff=staff,insights=insights, staffAvailable={staff!=null && holder.state.value.user?.username=="staff.demo" && holder.state.value.selected!=null},management=accountCoordinator, deletionPreview={ids->DemoDeletionPreview(ids,{id->holder.state.value.memberships.firstOrNull {it.id==id}?.slug?.let {org.spaceworks.app.ui.makerspaceLabel(it)}}, {id->staff!=null && holder.state.value.user?.username=="staff.demo" && holder.state.value.selectedId==id})}) @@ -79,7 +80,7 @@ class DemoAuthGateway:AuthGateway { return MFACompleted(me(),"demo-access","device",refresh="demo-refresh",deviceGrant=mapOf("id" to JsonPrimitive("00000000-0000-0000-0000-000000000001")),recoveryCodes=if(enrollment) listOf("DEMO-001-ABCD","DEMO-002-EFGH","DEMO-003-IJKL") else null) } override suspend fun me():AuthUserPayload=AuthUserPayload( - id=1,username=username,email="meera@example.invalid",displayName=profileNames[username] ?: registrations[username] ?: if(username.contains("staff")) "Arjun Rao" else "Meera Nair", + sessionAuthority=if(username.contains("staff"))"native_staff" else "member",id=1,username=username,email="meera@example.invalid",displayName=profileNames[username] ?: registrations[username] ?: if(username.contains("staff")) "Arjun Rao" else "Meera Nair", phone="",emailVerified=!username.contains("unverified") || verified,role="member",isSuperuser=username.contains("superadmin"),mustChangePassword=username.contains("temporary") && !passwordChanged, makerspaces=if(username.contains("empty") || registrations.containsKey(username))emptyList() else if(username.contains("single"))listOf(membership(1,"fab-lab-kochi")) else listOf(membership(1,"fab-lab-kochi"),membership(2,"maker-space-bengaluru"))) override suspend fun forgotPassword(email:String):String {if(email.contains("unavailable"))throw ApiFailure(503,"Recovery unavailable","recovery_unavailable",null);return "If the details are valid, a verification email has been sent."} @@ -89,7 +90,9 @@ class DemoAuthGateway:AuthGateway { override suspend fun resendEmail()="If the details are valid, a verification email has been sent." override suspend fun logout() {beforeSignOut();delay(100);profileNames.clear()} override suspend fun changePassword(current:String,replacement:String) {delay(200);passwordChanged=true} - private fun membership(id:Int,slug:String)=AuthMembership(id,slug,"member",1,"Member","member",AuthMembershipSourceEnum.membership,emptyList(),false,false,false,false,null,false) + private fun membership(id:Int,slug:String)=AuthMembership(id=id,slug=slug,role="member",roleId=1,roleIds=listOf(1),roleSlugs=listOf("member"), + roleNames=listOf("Member"),roleName="Member",roleSlug="member",source=AuthMembershipSourceEnum.membership,actions=emptyList(), + canConfigureMachineTypes=false,isMachineOnly=false,canRefer=false,canVerify=false,verifiedAt=null,referralsEnabled=false) private fun grant()=DeviceGrant(java.util.UUID.fromString("00000000-0000-0000-0000-000000000001"),"android","org.spaceworks.app","development","active",java.time.OffsetDateTime.parse("2026-10-16T00:00:00Z"),java.time.OffsetDateTime.parse("2026-10-16T00:00:00Z"),java.time.OffsetDateTime.parse("2026-10-16T00:00:00Z")) } diff --git a/app/src/debug/java/org/spaceworks/app/insights/DemoInsightsGateway.kt b/app/src/debug/java/org/spaceworks/app/insights/DemoInsightsGateway.kt new file mode 100644 index 0000000..75939d6 --- /dev/null +++ b/app/src/debug/java/org/spaceworks/app/insights/DemoInsightsGateway.kt @@ -0,0 +1,29 @@ +package org.spaceworks.app.insights + +import kotlinx.coroutines.delay +import org.spaceworks.app.member.MemberScope + +/** Debug-only sample insights. No API, SMS provider or consent ledger is ever contacted. */ +class DemoInsightsGateway(private val staff:()->Boolean={false}):InsightsGateway { + private var sms=ConsentChannel(true,false,true,"•••• ••• 3210",false) + private var whatsapp=ConsentChannel(true,false,false,"",false) + private fun months(vararg values:Int)=UsageSeries(listOf("Jul","Aug","Sep","Oct").zip(values.toList()).map {(m,v)->UsagePoint("$m 2026",v.toString(),v.toDouble())},"","Sample monthly activity") + override suspend fun usage(scope:MemberScope):UsageSummary { + delay(150) + return UsageSummary(true,LoanUsage(12,10,2,months(2,4,3,3)), + MachineUsage("7.5",UsageSeries(listOf(UsagePoint("Sep 2026","3.0",3.0),UsagePoint("Oct 2026","4.5",4.5)),"h","Sample machine hours"), + UsageSeries(listOf(UsagePoint("Laser cutter","4.5",4.5),UsagePoint("3D printer","3.0",3.0)),"h","Sample top machines")), + CountUsage(5,months(1,1,2,1)),3,PaymentUsage(mapOf("inr" to "1250.00"),mapOf("inr" to "250.00"))) + } + override suspend fun dashboard(scope:MemberScope):StaffDashboard { + delay(150) + return StaffDashboard(StaffScope.FULL,listOf(StaffCounter("pending_requests",3),StaffCounter("awaiting_issue",2),StaffCounter("overdue_loans",1)), + emptyList(),listOf(StaffWidget("loans_trend","Loans this month","count","18")),mapOf("inr" to "250.00")) + } + override suspend fun messaging(scope:MemberScope):MessagingConsent {delay(100);return MessagingConsent(sms,whatsapp)} + override suspend fun consent(scope:MemberScope,channel:String,optIn:Boolean):MessagingConsent { + delay(200) + if(channel=="sms")sms=sms.copy(consented=optIn) else whatsapp=whatsapp.copy(consented=optIn && whatsapp.phoneVerified) + return MessagingConsent(sms,whatsapp) + } +} diff --git a/app/src/debug/java/org/spaceworks/app/staff/DemoStaffGateway.kt b/app/src/debug/java/org/spaceworks/app/staff/DemoStaffGateway.kt index 7790758..492470b 100644 --- a/app/src/debug/java/org/spaceworks/app/staff/DemoStaffGateway.kt +++ b/app/src/debug/java/org/spaceworks/app/staff/DemoStaffGateway.kt @@ -156,7 +156,7 @@ class DemoStaffGateway internal constructor(private val readFixture:(String)->St override suspend fun events(scope:MemberScope,page:Int):EventListWithDefaults{refusal();val row=patch(obj("event"),"makerspace_id" to JsonPrimitive(scope.id));return decode(patch(envelope(if(scenario==StaffDemoScenario.EMPTY)emptyList()else listOf(row)),"default_registration_audience" to JsonPrimitive("members")))} override suspend fun resolveCheckIn(scope:MemberScope,eventId:Int,body:EventCheckInResolveRequest):EventCheckInResolveResponse{refusal();if(body.checkinToken!="DEMO-CHECKIN")throw ApiFailure(404,"This is not a sample check-in code for this event.",null,null) return decode(patch(obj("checkin"),"status" to JsonPrimitive(if(workspace(scope).attended)"attended"else "registered"),"confirmable" to JsonPrimitive(!workspace(scope).attended)))} - override suspend fun markAttended(scope:MemberScope,registrationId:Int,body:EventAttendanceMark):EventRegistrationAdmin{refusal();val s=workspace(scope);if(s.attended)throw ApiFailure(409,"Attendance was already recorded.",null,null);s.attended=true + override suspend fun markAttended(scope:MemberScope,registrationId:Int,body:EventAttendanceMark):EventAttendanceResult{refusal();val s=workspace(scope);if(s.attended)throw ApiFailure(409,"Attendance was already recorded.",null,null);s.attended=true return decode(patch(obj("registration"),"status" to JsonPrimitive("attended")))} override suspend fun spaces(scope:MemberScope,page:Int):BookableSpaceListResponse{refusal();return decode(envelope(if(scenario==StaffDemoScenario.EMPTY)emptyList()else listOf(patch(obj("space"),"makerspace_id" to JsonPrimitive(scope.id)))))} override suspend fun bookings(scope:MemberScope,spaceId:Int,page:Int):BookingListResponse{refusal();return decode(envelope(if(scenario==StaffDemoScenario.EMPTY)emptyList()else workspace(scope).bookings.values.toList()))} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 46ceac2..72330cd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ - + @@ -16,7 +16,12 @@ - + + + + + + diff --git a/app/src/main/java/org/spaceworks/app/AppController.kt b/app/src/main/java/org/spaceworks/app/AppController.kt index 56d1ddc..c370a7c 100644 --- a/app/src/main/java/org/spaceworks/app/AppController.kt +++ b/app/src/main/java/org/spaceworks/app/AppController.kt @@ -14,6 +14,7 @@ import org.spaceworks.app.network.* import org.spaceworks.app.notifications.* import org.spaceworks.app.platform.* import org.spaceworks.generated.model.EnvironmentEnum +import org.spaceworks.app.copy.apiFailure sealed interface PendingNativeIntent { val sequence:Long @@ -64,17 +65,21 @@ class AppController(application:Application):AndroidViewModel(application) { var actions:org.spaceworks.app.actions.MemberActionsStateHolder?=null var payments:org.spaceworks.app.payments.PaymentsStateHolder?=null var staff:org.spaceworks.app.staff.StaffStateHolder?=null + var insights:org.spaceworks.app.insights.InsightsStateHolder?=null val session=ApiSession(ServerAddress.parse(origin,BuildConfig.DEBUG),store){holder.state.value.selectedId?.toString()} val environment=if(BuildConfig.DEBUG)EnvironmentEnum.development else EnvironmentEnum.production val emulator=Build.FINGERPRINT.startsWith("generic") || Build.MODEL.contains("Emulator") || Build.MODEL.contains("sdk_gphone") || Build.PRODUCT.startsWith("sdk_") - val gateway=GeneratedAuthGateway(session,BuildConfig.APPLICATION_ID,environment,DevelopmentAttestation(BuildConfig.DEBUG,emulator,text=copyText), + val integrity=BuildConfig.PLAY_INTEGRITY_PROJECT.takeIf {it>0}?.let {PlayIntegrityProofProvider(application,it)} + val attestation=NativeAttestation(DevelopmentAttestation(BuildConfig.DEBUG,emulator,text=copyText),integrity?.let {provider->{challenge:String->provider.proof(challenge)}},copyText) + val gateway=GeneratedAuthGateway(session,BuildConfig.APPLICATION_ID,environment,attestation, onNativeSignOut={try {GoogleIdentity.clear(application)}catch(cancelled:kotlinx.coroutines.CancellationException){throw cancelled}catch(_:Exception){}}, onBeforeOwnedSignOut={epoch->push?.disable(forgetConsent=false,expectedEpoch=epoch)},text=copyText) holder=AuthStateHolder(gateway,store, readSelection={prefs.getInt("space.$origin",-1).takeIf {it>0}}, saveSelection={id->prefs.edit().apply {if(id==null)remove("space.$origin")else putInt("space.$origin",id)}.apply()}, - onSessionEnded={accounts?.clear();runtime?.let(management::sessionEnded);staff?.resetAccount();member?.clear();actions?.clear();payments?.clear();links?.clear();push?.pause()}, - onScopeChanged={runtime?.let(management::scopeChanged);staff?.clear();member?.clear();actions?.clear();payments?.clear()},text=copyText) + onSessionEnded={insights?.clear();accounts?.clear();runtime?.let(management::sessionEnded);staff?.resetAccount();member?.clear();actions?.clear();payments?.clear();links?.clear();push?.pause()}, + onScopeChanged={insights?.clear();runtime?.let(management::scopeChanged);staff?.clear();member?.clear();actions?.clear();payments?.clear()},text=copyText, + appVersion=BuildConfig.VERSION_NAME) session.bindIdentityEpoch {holder.acceptedInstallationEpoch} links=LinkStateHolder(session.server,holder::authorizeMakerspace,{id->if(staff?.protectsNavigation==true || management.protectsNavigation())throw kotlinx.coroutines.CancellationException();holder.select(id)},holder::authorizeSlug,text=copyText) push=PushStateHolder(GeneratedPushGateway(session,environment),object:PushProvider { @@ -128,8 +133,19 @@ class AppController(application:Application):AndroidViewModel(application) { {holder.state.value.user?.id?.toString()}, {holder.state.value.selected?.let {org.spaceworks.app.member.MemberScope(it.id,it.slug)}}, holder::endLocalSession,holder::foreground,holder::dropConfirmedMembership) + insights=org.spaceworks.app.insights.InsightsStateHolder(org.spaceworks.app.insights.GeneratedInsightsGateway(session), + failureText={copyText.apiFailure(it)},unavailableText={copyText(org.spaceworks.app.copy.CoreStrings.CORE_COULDN_T_CONNECT_TRY_AGAIN)},onUnauthorized={holder.foreground()}) + val deletedNotice=application.getString(R.string.deletion_deleted) val bundle=AuthBundle(holder,store,accounts=accounts,member=member,actions=actions,origin=session.server.toString(),push=push,links=links,pushConfigured=FcmRegistration.configured(application),sessionEpoch={store.snapshot().epoch},payments=payments,staff=staff, - staffAvailable={holder.state.value.staffAllowed && holder.state.value.selected!=null},management=management) + staffAvailable={holder.state.value.staffAllowed && holder.state.value.selected!=null},management=management, + deletionPreview={_-> + if(holder.acceptedInstallationEpoch==null)null else GeneratedDeletionFlow( + AccountDeletionStateHolder(GeneratedAccountDeletionGateway(session) {holder.acceptedInstallationEpoch!=null}, + onDeleted={holder.endLocalSession(deletedNotice)},failureText={copyText.apiFailure(it)}), + {id->holder.state.value.memberships.firstOrNull {it.id==id}?.slug?.let {org.spaceworks.app.ui.makerspaceLabel(it)}}, + {id->id!=null && holder.state.value.staffAllowed && holder.state.value.memberships.any {it.id==id}}, + session.server.host,{holder.state.value.googleClientId}) + },insights=insights) management.register(bundle,requireNotNull(runtime)) {owner->GeneratedAccountGateway(session,owner.hasSession,owner.selected,application.resources::accountText)} viewModelScope.launch {store.installationEpoch.collect {holder.invalidateInstallation(it)}} return bundle diff --git a/app/src/main/java/org/spaceworks/app/MainActivity.kt b/app/src/main/java/org/spaceworks/app/MainActivity.kt index 095ce8c..cd51538 100644 --- a/app/src/main/java/org/spaceworks/app/MainActivity.kt +++ b/app/src/main/java/org/spaceworks/app/MainActivity.kt @@ -38,14 +38,17 @@ class MainActivity:ComponentActivity() { super.onCreate(savedInstanceState) // Activity Result contracts must be registered before STARTED, including restored callbacks. val stripe=StripeSheetAdapter(this) - RazorpaySheetAdapter(this,stripe.callbacks) - val checkout=NativeCheckoutBindings(stripe.callbacks.state,{stripe.launch(it,false)}, + val razorpay=RazorpaySheetAdapter(this,stripe.callbacks) + val checkout=NativeCheckoutBindings(stripe.callbacks.state,{input->when(input){ + is StripeSheetInput->stripe.launch(input,false) + is RazorpaySheetInput->razorpay.launch(input,false) + }}, {stripe.callbacks.acknowledgeServerRefresh()}, {stripe.callbacks.recoverInterrupted(MobileText.resources(applicationContext)(org.spaceworks.app.payments.PaymentStrings.PAYMENT_CHECK_THE_PAYMENT_STATUS_BEFORE_TRYING_AGAIN));Unit}) lifecycleScope.launch(kotlinx.coroutines.Dispatchers.IO) { NativeDocumentFiles(java.io.File(cacheDir,"native-documents")).cleanStale() } - controller.receive(intent) + if(!OidcRedirects.consume(intent,applicationContext.packageName))controller.receive(intent) setContent { val incoming by controller.incoming.collectAsStateWithLifecycle() CompositionLocalProvider(LocalNativeCheckout provides checkout) { @@ -53,7 +56,15 @@ class MainActivity:ComponentActivity() { } } } - override fun onNewIntent(intent:Intent){super.onNewIntent(intent);setIntent(intent);controller.receive(intent)} + override fun onNewIntent(intent:Intent){super.onNewIntent(intent);setIntent(intent) + if(!OidcRedirects.consume(intent,applicationContext.packageName))controller.receive(intent)} + private var oidcCheck:kotlinx.coroutines.Job?=null + override fun onResume() { + super.onResume() + // A redirect arrives through onNewIntent before resume; still waiting means the browser was dismissed. + if(OidcRedirects.waiting){oidcCheck?.cancel();oidcCheck=lifecycleScope.launch {kotlinx.coroutines.delay(750);OidcRedirects.abandon()}} + } + override fun onPause(){oidcCheck?.cancel();super.onPause()} } @Composable fun SpaceWorksApp(holder:ConnectionStateHolder,sessionFactory:((String)->AuthBundle)?=null, demoFactory:()->AuthBundle?={DemoSupport.create()},onExitDemo:()->Unit={},onboarding:OnboardingStateHolder?=null,appearance:AppearanceStateHolder?=null,incoming:PendingNativeIntent?=null,onIncomingConsumed:(Long)->Unit={}) { @@ -111,7 +122,7 @@ class MainActivity:ComponentActivity() { val management=bundle.management val completion=management?.owner?.completion?.collectAsStateWithLifecycle()?.value var accountPanel by remember(bundle){mutableStateOf(null)} - var deletion by remember(bundle){mutableStateOf(null)} + var deletion by remember(bundle){mutableStateOf(null)} var pendingStaffMaker by remember(bundle){mutableStateOf(null)} var accountError by remember(bundle){mutableStateOf(null)} val panelState=accountPanel?.state?.collectAsStateWithLifecycle()?.value @@ -232,7 +243,7 @@ class MainActivity:ComponentActivity() { state.user?.mustChangePassword==true -> ChangePasswordScreen(bundle) accountPanel!=null && management?.owner?.current()===accountPanel -> AccountScreens(requireNotNull(accountPanel),bundle.demo,::closeAccount, privacyURL=BrandLinks.privacyURL,supportURL=BrandLinks.supportURL, - onDemoDelete=if(bundle.demo){{deletion=bundle.deletionPreview(bundle.holder.state.value.memberships.map {it.id})}}else null) + onDelete={deletion=bundle.deletionPreview(bundle.holder.state.value.memberships.map {it.id})}) state.selected==null -> MakerspacePicker(bundle,if(management!=null){::openAccount}else null) else -> MemberShell(bundle,onChangeServer,onShowIntro,if(management!=null){::openAccount}else null, openStaff=pendingStaffMaker==state.selectedId,onStaffOpened={pendingStaffMaker=null}) diff --git a/app/src/main/java/org/spaceworks/app/account/AccountDeletion.kt b/app/src/main/java/org/spaceworks/app/account/AccountDeletion.kt new file mode 100644 index 0000000..a58f0e7 --- /dev/null +++ b/app/src/main/java/org/spaceworks/app/account/AccountDeletion.kt @@ -0,0 +1,238 @@ +package org.spaceworks.app.account + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.serialization.json.Json +import org.spaceworks.app.auth.requireBody +import org.spaceworks.app.network.ApiFailure +import org.spaceworks.app.network.ApiSession +import org.spaceworks.app.network.Errors +import org.spaceworks.generated.api.AccountsApi +import org.spaceworks.generated.api.AuthApi +import org.spaceworks.generated.api.ReauthenticationApi +import org.spaceworks.generated.model.AccountDeletionRefused +import org.spaceworks.generated.model.AccountDeletionRequest +import org.spaceworks.generated.model.ConfirmEnum +import org.spaceworks.generated.model.Reauthentication +import org.spaceworks.generated.model.ReauthenticationFactor +import org.spaceworks.generated.model.ReauthenticationResult +import org.spaceworks.generated.model.ReauthenticationSocialComplete +import org.spaceworks.generated.model.ReauthenticationSocialStart +import org.spaceworks.generated.model.AccountDeletionBlocker as BlockerWire +import org.spaceworks.generated.model.AccountDeletionPreview as PreviewWire + +/** Immutable screen projection of one server blocker; unknown codes keep their server detail. */ +data class DeletionBlocker(val code:String,val makerspaceId:Int?,val makerspaceSlug:String?,val organizationUrl:String?,val count:Int,val detail:String) { + val fix:AccountFixDestination get()=when(code) { + "unpaid_charges","refund_in_progress","deposit_held" -> AccountFixDestination.PAYMENTS + "active_loans","open_requests","problem_report_open" -> AccountFixDestination.ACTIVITY + "upcoming_bookings" -> AccountFixDestination.BOOKINGS + "upcoming_registrations" -> AccountFixDestination.EVENTS + "last_manager" -> AccountFixDestination.STAFF + else -> AccountFixDestination.SUPPORT + } +} +data class DeletionPreview(val deletable:Boolean,val restorable:Boolean,val restoreReason:String,val blockers:List, + val reauthRequired:Boolean,val methods:List) +sealed interface DeletionProof { + data class Verified(val expiresInSeconds:Int):DeletionProof + /** A provider proof that still needs a second factor of [kind] (totp, phone or email_code). */ + data class Factor(val token:String,val kind:String):DeletionProof { override fun toString()="Factor(redacted)" } +} +class PhoneProofToken(val token:String) { override fun toString()="PhoneProofToken(redacted)" } +class SocialProofIntent(val provider:String,val state:String,val nonce:String) { override fun toString()="SocialProofIntent(redacted)" } +sealed interface DeletionOutcome { + data class Deleted(val deletedAt:String,val purgeAfter:String):DeletionOutcome + data class Blocked(val blockers:List):DeletionOutcome +} + +interface AccountDeletionGateway { + suspend fun preview():DeletionPreview + suspend fun password(password:String,code:String?):DeletionProof + suspend fun startPhone():PhoneProofToken + suspend fun confirmPhone(token:PhoneProofToken,code:String):DeletionProof + suspend fun startSocial(provider:String):SocialProofIntent + suspend fun completeSocial(intent:SocialProofIntent,idToken:String):DeletionProof + suspend fun factor(token:String,code:String):DeletionProof + suspend fun delete():DeletionOutcome +} + +/** Account-global generated operations only: no makerspace header and no fabricated scope. */ +class GeneratedAccountDeletionGateway(private val session:ApiSession,private val hasSession:()->Boolean):AccountDeletionGateway { + private val owningEpoch=session.installationEpoch + private val json=Json {ignoreUnknownKeys=true;explicitNulls=false} + private fun api(type:Class):T { + if(!hasSession())throw ApiFailure(401,"Please sign in again.","session_expired",null) + if(session.installationEpoch!=owningEpoch)throw CancellationException("Account installation changed") + return session.pinnedGlobalApi(type,owningEpoch) {call->if(session.installationEpoch!=owningEpoch)call.cancel()} + } + private fun blocker(row:BlockerWire)=DeletionBlocker(row.code,row.makerspaceId,row.makerspaceSlug,row.organizationUrl,row.count,row.detail) + private fun proof(result:ReauthenticationResult):DeletionProof { + val token=result.factorToken + val kind=result.factorRequired?.value + if(!result.verified && token!=null && kind!=null)return DeletionProof.Factor(token,kind) + if(!result.verified)throw ApiFailure(0,"Verification failed. Check your password or code.","reauth_failed",null) + return DeletionProof.Verified(result.expiresIn) + } + /** A wrong password is also 401: after the core's single rotation, a valid identity means it was the credential. */ + private suspend fun verifying(block:suspend()->T):T = try {block()} catch(failure:ApiFailure) { + if(failure.status!=401 || !hasSession())throw failure + try {api(AuthApi::class.java).apiV1AuthMeRetrieve().requireBody()} + catch(cancelled:CancellationException){throw cancelled} catch(_:Exception){throw failure} + throw failure.copy(status=0,code="reauth_failed") + } + override suspend fun preview():DeletionPreview { + val value:PreviewWire=api(AccountsApi::class.java).apiV1AuthAccountDeletionRetrieve().requireBody() + return DeletionPreview(value.deletable,value.restorable,value.restoreReason,value.blockers.map(::blocker),value.reauth.required,value.reauth.methods) + } + override suspend fun password(password:String,code:String?)=verifying { + proof(api(ReauthenticationApi::class.java).apiV1AuthReauthCreate(Reauthentication(password.takeIf {it.isNotEmpty()},code?.takeIf {it.isNotBlank()})).requireBody()) + } + override suspend fun startPhone()=PhoneProofToken(api(ReauthenticationApi::class.java).apiV1AuthReauthPhoneStartCreate().requireBody().token) + override suspend fun confirmPhone(token:PhoneProofToken,code:String)=verifying { + proof(api(ReauthenticationApi::class.java).apiV1AuthReauthPhoneConfirmCreate(ReauthenticationFactor(token.token,code)).requireBody()) + } + override suspend fun startSocial(provider:String):SocialProofIntent { + val started=api(ReauthenticationApi::class.java).apiV1AuthReauthSocialStartCreate(ReauthenticationSocialStart(provider)).requireBody() + return SocialProofIntent(provider,started.state,started.nonce) + } + override suspend fun completeSocial(intent:SocialProofIntent,idToken:String)=verifying { + proof(api(ReauthenticationApi::class.java).apiV1AuthReauthSocialCompleteCreate(ReauthenticationSocialComplete(intent.state,intent.nonce,idToken=idToken)).requireBody()) + } + override suspend fun factor(token:String,code:String)=verifying { + proof(api(ReauthenticationApi::class.java).apiV1AuthReauthSocialFactorCreate(ReauthenticationFactor(token,code)).requireBody()) + } + override suspend fun delete():DeletionOutcome { + val response=api(AccountsApi::class.java).apiV1AuthAccountDeletionCreate(AccountDeletionRequest(ConfirmEnum.entries.single {it.value=="DELETE"})) + if(response.isSuccessful) { + val body=response.body()?:throw IllegalStateException("Empty API response") + return DeletionOutcome.Deleted(body.deletedAt.toString(),body.purgeAfter.toString()) + } + val raw=response.errorBody()?.string() + // 409 and a blocker-shaped 403 carry a fresh server list; anything else is an ordinary failure. + if(response.code() in setOf(403,409))runCatching {json.decodeFromString(AccountDeletionRefused.serializer(),requireNotNull(raw))}.getOrNull() + ?.let {return DeletionOutcome.Blocked(it.blockers.map(::blocker))} + throw Errors.map(response.code(),raw,response.headers()["Retry-After"]) + } +} + +enum class DeletionStage { LOADING, BLOCKERS, REAUTHENTICATE, FACTOR, CONFIRM, FINISHED } +enum class DeletionMessage { PROOF_FAILED, PROOF_EXPIRED, CONFIRM_INVALID, CONFLICT, CODE_REQUIRED, PASSWORD_REQUIRED, PROVIDER_UNAVAILABLE } +data class DeletionState(val stage:DeletionStage=DeletionStage.LOADING,val preview:DeletionPreview?=null,val busy:Boolean=false, + val phoneCodeSent:Boolean=false,val factorKind:String?=null,val proofExpiresAt:Long?=null, + val message:DeletionMessage?=null,val error:String?=null,val retryAtMillis:Long=0,val deleted:DeletionOutcome.Deleted?=null) { + val methods get()=preview?.methods.orEmpty() + val passwordAvailable get()="password" in methods + val totpRequired get()="totp" in methods + val phoneAvailable get()="phone" in methods + /** Native reauthentication supports linked Google identities; other providers verify on the web. */ + val socialProviders get()=methods.filter {it=="google"} +} + +/** Server-authoritative whole-account deletion. Credentials and codes are arguments only, never state. */ +class AccountDeletionStateHolder(private val api:AccountDeletionGateway,private val now:()->Long=System::currentTimeMillis, + private val onDeleted:suspend(DeletionOutcome.Deleted)->Unit={},private val failureText:(ApiFailure)->String={it.detail}) { + private val mutable=MutableStateFlow(DeletionState());val state=mutable.asStateFlow() + private var phoneToken:PhoneProofToken?=null + private var factorToken:String?=null + private var generation=0L + fun clear(){generation++;phoneToken=null;factorToken=null;mutable.value=DeletionState()} + private suspend fun operation(block:suspend(Long)->Unit) { + if(state.value.busy || now() mutable.update {it.copy(message=DeletionMessage.PROOF_FAILED,retryAtMillis=retry)} + failure.code=="recent_reauth_required" -> mutable.update {it.copy(stage=DeletionStage.REAUTHENTICATE,proofExpiresAt=null,message=DeletionMessage.PROOF_EXPIRED)} + failure.code=="deletion_retry" -> {mutable.update {it.copy(error=failureText(failure))};reload(owner)} + else -> mutable.update {it.copy(error=failureText(failure),retryAtMillis=retry)} + } + } + catch(_:Exception){if(owner==generation)mutable.update {it.copy(message=DeletionMessage.PROVIDER_UNAVAILABLE)}} + finally {if(owner==generation)mutable.update {it.copy(busy=false)}} + } + private suspend fun reload(owner:Long) { + val preview=api.preview() + if(owner!=generation)return + val stage=when { + !preview.deletable -> DeletionStage.BLOCKERS + preview.reauthRequired -> DeletionStage.REAUTHENTICATE + else -> DeletionStage.CONFIRM + } + mutable.update {it.copy(stage=stage,preview=preview,phoneCodeSent=false,factorKind=null,proofExpiresAt=if(stage==DeletionStage.CONFIRM)it.proofExpiresAt else null)} + } + suspend fun load()=operation {reload(it)} + private suspend fun accept(owner:Long,proof:DeletionProof) { + if(owner!=generation)return + when(proof) { + is DeletionProof.Factor -> {factorToken=proof.token;mutable.update {it.copy(stage=DeletionStage.FACTOR,factorKind=proof.kind)}} + is DeletionProof.Verified -> { + phoneToken=null;factorToken=null + mutable.update {it.copy(proofExpiresAt=now()+proof.expiresInSeconds.coerceAtLeast(0)*1000L)} + // Blockers can appear while verifying; the confirmation step follows a fresh preview. + reload(owner) + if(state.value.stage==DeletionStage.REAUTHENTICATE)mutable.update {it.copy(stage=DeletionStage.CONFIRM)} + } + } + } + suspend fun verifyPassword(password:String,code:String)=operation {owner-> + if(state.value.stage!=DeletionStage.REAUTHENTICATE)return@operation + val s=state.value + if(s.passwordAvailable && password.isEmpty()){mutable.update {it.copy(message=DeletionMessage.PASSWORD_REQUIRED)};return@operation} + if(s.totpRequired && !Regex("\\d{6}").matches(code.trim())){mutable.update {it.copy(message=DeletionMessage.CODE_REQUIRED)};return@operation} + accept(owner,api.password(password,code.trim().takeIf {s.totpRequired})) + } + suspend fun sendPhoneCode()=operation {owner-> + if(state.value.stage!=DeletionStage.REAUTHENTICATE)return@operation + val token=api.startPhone() + if(owner==generation){phoneToken=token;mutable.update {it.copy(phoneCodeSent=true)}} + } + suspend fun verifyPhone(code:String)=operation {owner-> + if(state.value.stage!=DeletionStage.REAUTHENTICATE)return@operation + val token=phoneToken + if(token==null){mutable.update {it.copy(phoneCodeSent=false)};return@operation} + if(!Regex("\\d{6}").matches(code.trim())){mutable.update {it.copy(message=DeletionMessage.CODE_REQUIRED)};return@operation} + accept(owner,api.confirmPhone(token,code.trim())) + } + /** [credential] obtains a fresh provider ID token bound to the server nonce. */ + suspend fun verifySocial(provider:String,credential:suspend(String)->String)=operation {owner-> + if(state.value.stage!=DeletionStage.REAUTHENTICATE)return@operation + val intent=api.startSocial(provider) + if(owner!=generation)return@operation + accept(owner,api.completeSocial(intent,credential(intent.nonce))) + } + suspend fun verifyFactor(code:String)=operation {owner-> + if(state.value.stage!=DeletionStage.FACTOR)return@operation + val token=factorToken?:run {mutable.update {it.copy(stage=DeletionStage.REAUTHENTICATE,factorKind=null)};return@operation} + if(code.trim().length !in 6..64){mutable.update {it.copy(message=DeletionMessage.CODE_REQUIRED)};return@operation} + accept(owner,api.factor(token,code.trim())) + } + fun expireProof() { + val expires=state.value.proofExpiresAt?:return + if(state.value.stage==DeletionStage.CONFIRM && now()>=expires) + mutable.update {it.copy(stage=DeletionStage.REAUTHENTICATE,proofExpiresAt=null,message=DeletionMessage.PROOF_EXPIRED)} + } + suspend fun confirm(typed:String)=operation {owner-> + expireProof() + if(state.value.stage!=DeletionStage.CONFIRM)return@operation + if(typed!="DELETE"){mutable.update {it.copy(message=DeletionMessage.CONFIRM_INVALID)};return@operation} + when(val outcome=api.delete()) { + is DeletionOutcome.Blocked -> if(owner==generation)mutable.update { + it.copy(stage=DeletionStage.BLOCKERS,preview=it.preview?.copy(deletable=false,blockers=outcome.blockers),proofExpiresAt=null,message=DeletionMessage.CONFLICT) + } + is DeletionOutcome.Deleted -> { + if(owner!=generation)return@operation + mutable.update {it.copy(stage=DeletionStage.FINISHED,deleted=outcome,proofExpiresAt=null)} + onDeleted(outcome) + } + } + } + fun backToMethods(){if(!state.value.busy){phoneToken=null;factorToken=null;mutable.update {it.copy(stage=DeletionStage.REAUTHENTICATE,phoneCodeSent=false,factorKind=null,message=null)}}} +} diff --git a/app/src/main/java/org/spaceworks/app/account/AccountDeletionPreview.kt b/app/src/main/java/org/spaceworks/app/account/AccountDeletionPreview.kt index 5a50ad7..6d2e0c8 100644 --- a/app/src/main/java/org/spaceworks/app/account/AccountDeletionPreview.kt +++ b/app/src/main/java/org/spaceworks/app/account/AccountDeletionPreview.kt @@ -1,11 +1,4 @@ package org.spaceworks.app.account -import androidx.compose.runtime.Composable - +/** In-app destinations that resolve a deletion blocker; organization fixes stay on the web console. */ enum class AccountFixDestination { PAYMENTS, ACTIVITY, BOOKINGS, EVENTS, STAFF, SUPPORT } - -/** Variant-provided preview only. Release supplies no implementation or deletion transport. */ -interface AccountDeletionPreview { - fun clear() - @Composable fun Screen(onBack:()->Unit,onFix:(Int?,AccountFixDestination)->Unit,onDeleted:()->Unit) -} diff --git a/app/src/main/java/org/spaceworks/app/account/AccountDeletionScreen.kt b/app/src/main/java/org/spaceworks/app/account/AccountDeletionScreen.kt new file mode 100644 index 0000000..8eba1d9 --- /dev/null +++ b/app/src/main/java/org/spaceworks/app/account/AccountDeletionScreen.kt @@ -0,0 +1,177 @@ +package org.spaceworks.app.account + +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.* +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.launch +import org.spaceworks.app.R +import org.spaceworks.app.ui.* + +/** Variant-independent whole-account deletion entry. Debug supplies a preview; release the generated flow. */ +interface AccountDeletionFlow { + fun clear() + @Composable fun Screen(onBack:()->Unit,onFix:(Int?,AccountFixDestination)->Unit,onDeleted:()->Unit) +} + +/** Production deletion backed by [AccountDeletionStateHolder] and generated operations. */ +class GeneratedDeletionFlow(private val holder:AccountDeletionStateHolder,private val makerName:(Int)->String?, + private val canOpenStaff:(Int?)->Boolean,private val trustedHost:String?, + private val googleClientId:()->String?):AccountDeletionFlow { + override fun clear()=holder.clear() + @Composable override fun Screen(onBack:()->Unit,onFix:(Int?,AccountFixDestination)->Unit,onDeleted:()->Unit) { + val context=androidx.compose.ui.platform.LocalContext.current + val unavailable=stringResource(R.string.deletion_provider_unavailable) + val credential:(suspend(String)->String)?=googleClientId()?.let {client-> + val provider:suspend(String)->String={nonce->org.spaceworks.app.platform.GoogleIdentity.credential(context.activity()?:throw IllegalStateException(unavailable),client,nonce)} + provider} + AccountDeletionScreen(holder,onBack,onFix,makerName,canOpenStaff,trustedHost,credential) + } +} + +@Composable fun AccountDeletionScreen(holder:AccountDeletionStateHolder,onBack:()->Unit,onFix:(Int?,AccountFixDestination)->Unit, + makerName:(Int)->String?={null},canOpenStaff:(Int?)->Boolean={false},trustedHost:String?=null, + googleCredential:(suspend(String)->String)?=null) { + val s by holder.state.collectAsState() + val scope=rememberCoroutineScope() + val focus=LocalFocusManager.current + val keyboard=LocalSoftwareKeyboardController.current + val uri=androidx.compose.ui.platform.LocalUriHandler.current + var password by remember(holder,s.stage) {mutableStateOf("")} + var code by remember(holder,s.stage,s.phoneCodeSent) {mutableStateOf("")} + var confirmation by remember(holder,s.stage) {mutableStateOf("")} + LaunchedEffect(holder) {if(s.preview==null)holder.load()} + LaunchedEffect(s.stage,s.proofExpiresAt) {if(s.stage==DeletionStage.CONFIRM)while(true){kotlinx.coroutines.delay(500);holder.expireProof()}} + fun leave(){password="";code="";confirmation="";holder.clear();onBack()} + fun act(block:suspend()->Unit){focus.clearFocus();keyboard?.hide();scope.launch {block()}} + BackHandler(!s.busy) {leave()} + AuthLayout(stringResource(R.string.account_deletion),stringResource(R.string.deletion_explanation)) { + BrandTextButton(stringResource(R.string.account_back),::leave,!s.busy) + Text(stringResource(R.string.deletion_retention),color=MaterialTheme.colorScheme.onSurfaceVariant) + s.preview?.takeIf {it.restoreReason.isNotBlank()}?.let { + Text(stringResource(R.string.deletion_restore_title),style=MaterialTheme.typography.titleMedium) + Text(it.restoreReason) + } + val loading=stringResource(R.string.deletion_loading) + if(s.busy)LinearProgressIndicator(Modifier.fillMaxWidth().semantics {contentDescription=loading}) + s.message?.let {BrandError(stringResource(when(it) { + DeletionMessage.PROOF_FAILED->R.string.deletion_proof_failed + DeletionMessage.PROOF_EXPIRED->R.string.deletion_proof_expired + DeletionMessage.CONFIRM_INVALID->R.string.deletion_confirm_invalid + DeletionMessage.CONFLICT->R.string.deletion_conflict + DeletionMessage.CODE_REQUIRED->R.string.deletion_code_required + DeletionMessage.PASSWORD_REQUIRED->R.string.deletion_password_required + DeletionMessage.PROVIDER_UNAVAILABLE->R.string.deletion_provider_unavailable + }))} + BrandError(s.error) + when(s.stage) { + DeletionStage.LOADING -> { + Text(stringResource(R.string.deletion_loading),Modifier.semantics {liveRegion=LiveRegionMode.Polite}) + if(!s.busy)BrandButton(stringResource(R.string.deletion_recheck),{act {holder.load()}}) + } + DeletionStage.BLOCKERS -> { + Text(stringResource(R.string.deletion_blockers),style=MaterialTheme.typography.titleLarge,modifier=Modifier.semantics {heading()}) + s.preview?.blockers.orEmpty().forEach {blocker-> + OutlinedCard(Modifier.fillMaxWidth().testTag("deletion-blocker-${blocker.code}")) { + Column(Modifier.padding(20.dp),verticalArrangement=Arrangement.spacedBy(8.dp)) { + Text(stringResource(blockerLabel(blocker.code)),style=MaterialTheme.typography.titleMedium) + val where=blocker.makerspaceId?.let {makerName(it)?:blocker.makerspaceSlug?.let(::makerspaceLabel)?:stringResource(R.string.account_makerspace_id,it)} + ?:stringResource(R.string.deletion_this_server) + Text(stringResource(R.string.deletion_blocker_scope_count,where,pluralStringResource(R.plurals.deletion_outstanding_count,blocker.count,blocker.count))) + // Server detail is authoritative and the only text for unknown codes. + Text(blocker.detail) + val web=blocker.organizationUrl?.takeIf {AccountLinkPolicy.safe(it) && trustedHost!=null && java.net.URI(it).host.equals(trustedHost,true)} + when { + web!=null -> BrandTextButton(stringResource(R.string.deletion_open_web_console),{runCatching {uri.openUri(web)}}) + else -> { + val destination=if(blocker.fix==AccountFixDestination.STAFF && !canOpenStaff(blocker.makerspaceId))AccountFixDestination.SUPPORT else blocker.fix + BrandTextButton(stringResource(fixLabel(destination)),{holder.clear();onFix(blocker.makerspaceId,destination)}) + } + } + } + } + } + BrandButton(stringResource(R.string.deletion_recheck),{act {holder.load()}},!s.busy,Modifier.testTag("deletion-recheck")) + } + DeletionStage.REAUTHENTICATE -> { + Text(stringResource(R.string.deletion_proof_title),style=MaterialTheme.typography.titleLarge,modifier=Modifier.semantics {heading()}) + val usable=s.passwordAvailable || s.totpRequired || s.phoneAvailable || (s.socialProviders.isNotEmpty() && googleCredential!=null) + if(!usable)Text(stringResource(R.string.deletion_no_native_method)) + if(s.passwordAvailable || s.totpRequired) { + if(s.passwordAvailable && s.totpRequired)Text(stringResource(R.string.deletion_password_code_help)) + if(s.passwordAvailable)BrandField(password,{password=it.take(1024)},stringResource(R.string.deletion_password),Modifier.testTag("deletion-password"),!s.busy,secret=true) + if(s.totpRequired)BrandField(code,{code=it.filter {c->c.isDigit()}.take(6)},stringResource(R.string.deletion_authenticator),Modifier.testTag("deletion-totp"),!s.busy,role=FieldRole.ONE_TIME_CODE) + BrandButton(stringResource(R.string.deletion_proof_continue),{val p=password;val c=code;password="";code="";act {holder.verifyPassword(p,c)}},!s.busy,Modifier.testTag("deletion-authenticate")) + } + if(s.phoneAvailable) { + if(!s.phoneCodeSent)BrandTextButton(stringResource(R.string.deletion_send_code),{act {holder.sendPhoneCode()}},!s.busy,modifier=Modifier.testTag("deletion-phone-start")) + else { + Text(stringResource(R.string.deletion_code_sent)) + BrandField(code,{code=it.filter {c->c.isDigit()}.take(6)},stringResource(R.string.deletion_phone),Modifier.testTag("deletion-phone-code"),!s.busy,role=FieldRole.ONE_TIME_CODE) + BrandButton(stringResource(R.string.deletion_proof_continue),{val c=code;code="";act {holder.verifyPhone(c)}},!s.busy && code.length==6,Modifier.testTag("deletion-phone-verify")) + } + } + if(googleCredential!=null)s.socialProviders.forEach {provider-> + BrandTextButton(stringResource(R.string.deletion_verify_with,"Google"),{act {holder.verifySocial(provider,googleCredential)}},!s.busy) + } + } + DeletionStage.FACTOR -> { + Text(stringResource(R.string.deletion_factor_title),style=MaterialTheme.typography.titleLarge) + Text(stringResource(if(s.factorKind=="totp")R.string.deletion_factor_totp else R.string.deletion_factor_code)) + BrandField(code,{code=it.trim().take(64)},stringResource(R.string.deletion_code),Modifier.testTag("deletion-factor"),!s.busy,role=FieldRole.ONE_TIME_CODE) + BrandButton(stringResource(R.string.deletion_proof_continue),{val c=code;code="";act {holder.verifyFactor(c)}},!s.busy && code.isNotBlank()) + BrandTextButton(stringResource(R.string.deletion_use_another_method),holder::backToMethods,!s.busy) + } + DeletionStage.CONFIRM -> { + Text(stringResource(R.string.deletion_confirm_title),style=MaterialTheme.typography.titleLarge,modifier=Modifier.semantics {heading()}) + Text(stringResource(R.string.deletion_confirm_help)) + BrandField(confirmation,{confirmation=it.take(16)},stringResource(R.string.deletion_confirm_label),Modifier.testTag("deletion-confirmation"),!s.busy, + onDone={if(confirmation=="DELETE"){val typed=confirmation;confirmation="";act {holder.confirm(typed)}}}) + Button({val typed=confirmation;confirmation="";act {holder.confirm(typed)}},Modifier.fillMaxWidth().heightIn(min=48.dp).testTag("deletion-submit"), + enabled=!s.busy && confirmation=="DELETE",colors=ButtonDefaults.buttonColors(containerColor=MaterialTheme.colorScheme.error,contentColor=MaterialTheme.colorScheme.onError)) { + Text(stringResource(R.string.deletion_delete_account)) + } + } + DeletionStage.FINISHED -> { + Text(stringResource(R.string.deletion_deleted),Modifier.semantics {liveRegion=LiveRegionMode.Polite}) + s.deleted?.let {Text(stringResource(R.string.deletion_purge_after,it.purgeAfter.take(10)))} + } + } + } +} + +private fun blockerLabel(code:String)=when(code) { + "unpaid_charges"->R.string.deletion_unpaid + "refund_in_progress"->R.string.deletion_refund + "deposit_held"->R.string.deletion_deposit + "active_loans"->R.string.deletion_loans + "open_requests"->R.string.deletion_requests + "accountability_open"->R.string.deletion_accountability + "problem_report_open"->R.string.deletion_problem_report + "access_restricted"->R.string.deletion_access_restricted + "open_service_jobs"->R.string.deletion_service_jobs + "upcoming_bookings"->R.string.deletion_bookings + "upcoming_registrations"->R.string.deletion_events + "last_manager"->R.string.deletion_manager + "last_org_governor"->R.string.deletion_last_org_governor + "organization_owner"->R.string.deletion_owner + "makerspace_gated"->R.string.deletion_makerspace_gated + "superadmin"->R.string.deletion_superadmin + else->R.string.deletion_other +} +private fun fixLabel(destination:AccountFixDestination)=when(destination) { + AccountFixDestination.PAYMENTS->R.string.deletion_fix_payments + AccountFixDestination.ACTIVITY->R.string.deletion_fix_activity + AccountFixDestination.BOOKINGS->R.string.deletion_fix_bookings + AccountFixDestination.EVENTS->R.string.deletion_fix_events + AccountFixDestination.STAFF->R.string.deletion_fix_staff + AccountFixDestination.SUPPORT->R.string.deletion_fix_support +} diff --git a/app/src/main/java/org/spaceworks/app/account/AccountScreens.kt b/app/src/main/java/org/spaceworks/app/account/AccountScreens.kt index d09dd90..55ee433 100644 --- a/app/src/main/java/org/spaceworks/app/account/AccountScreens.kt +++ b/app/src/main/java/org/spaceworks/app/account/AccountScreens.kt @@ -48,7 +48,7 @@ private data class AccountConfirmation(val title: String, val detail: String, va } @Composable fun AccountScreens(holder: AccountManagementStateHolder, demo: Boolean, onBack: () -> Unit, - privacyURL: String? = null, supportURL: String? = null, now: () -> Long = System::currentTimeMillis, onDemoDelete:(()->Unit)?=null) { + privacyURL: String? = null, supportURL: String? = null, now: () -> Long = System::currentTimeMillis, onDelete:(()->Unit)?=null) { val s by holder.state.collectAsStateWithLifecycle() val work = rememberCoroutineScope() val focus = LocalFocusManager.current @@ -148,7 +148,7 @@ private data class AccountConfirmation(val title: String, val detail: String, va if (s.capabilitiesLoading) Text(stringResource(R.string.account_services_loading)) if (s.scope!=null && s.capabilities?.let { !it.selfService || !it.selfExport } == true) Text(stringResource(R.string.account_services_disabled)) if(s.scope!=null)BrandTextButton(stringResource(R.string.account_services_refresh), { submit { holder.loadCapabilities() } }, enabled && !s.capabilitiesLoading) - AccountPrivacyLinks(privacyURL, supportURL,onDemoDelete) + AccountPrivacyLinks(privacyURL, supportURL,demo,onDelete) } AccountPage.PASSWORD -> { Text(stringResource(R.string.account_password_intro)) @@ -276,7 +276,7 @@ private data class AccountConfirmation(val title: String, val detail: String, va private fun pageTitle(page: AccountPage) = when (page) { AccountPage.MENU -> R.string.account_title; AccountPage.PASSWORD -> R.string.account_change_password; AccountPage.SECURITY -> R.string.account_security; AccountPage.PHONE -> R.string.account_phone; AccountPage.PROVIDERS -> R.string.account_providers; AccountPage.SESSIONS -> R.string.account_sessions; AccountPage.DEVICES -> R.string.account_grants; AccountPage.MEMBERSHIP -> R.string.account_membership; AccountPage.EXPORTS -> R.string.account_exports } -@Composable private fun AccountPrivacyLinks(privacyURL: String?, supportURL: String?,onDemoDelete:(()->Unit)?) { +@Composable private fun AccountPrivacyLinks(privacyURL: String?, supportURL: String?,demo:Boolean,onDelete:(()->Unit)?) { val handler = LocalUriHandler.current var failed by remember { mutableStateOf(false) } AccountCard { @@ -287,8 +287,9 @@ private fun pageTitle(page: AccountPage) = when (page) { AccountPage.MENU -> R.s } if (failed) BrandError(stringResource(R.string.account_link_unavailable)) Text(stringResource(R.string.account_deletion), style = MaterialTheme.typography.titleMedium) - Text(stringResource(R.string.account_deletion_gap)) - onDemoDelete?.let {BrandTextButton(stringResource(R.string.account_deletion_preview),it,modifier=Modifier.testTag("account-deletion-preview"))} + Text(stringResource(if(onDelete!=null)R.string.account_deletion_available else R.string.account_deletion_gap)) + onDelete?.let {if(demo)BrandTextButton(stringResource(R.string.account_deletion_preview),it,modifier=Modifier.testTag("account-deletion-preview")) + else BrandTextButton(stringResource(R.string.deletion_delete_account),it,modifier=Modifier.testTag("account-deletion"))} } } diff --git a/app/src/main/java/org/spaceworks/app/actions/GeneratedMemberActionsGateway.kt b/app/src/main/java/org/spaceworks/app/actions/GeneratedMemberActionsGateway.kt index 27dd249..276d581 100644 --- a/app/src/main/java/org/spaceworks/app/actions/GeneratedMemberActionsGateway.kt +++ b/app/src/main/java/org/spaceworks/app/actions/GeneratedMemberActionsGateway.kt @@ -67,7 +67,10 @@ class GeneratedMemberActionsGateway(private val session: ApiSession,private val val (start, end) = interval(startsAtISO, endsAtISO) val result = api(PublicBookingsApi::class.java, scope).apiV1PublicSpacesBookCreate(makerspaceSlug = scope.slug, publicToken = uuid(publicToken), publicBookingInput = PublicBookingInput(startsAt = start, endsAt = end, customAnswers = answerObject(answers))).requireBody() - return result.status.value + return when (result) { + is ApiV1PublicSpacesBookCreate201Response.ApiV1PublicSpacesBookCreate201ResponseOneOfValue -> result.value.status.value + is ApiV1PublicSpacesBookCreate201Response.ApiV1PublicSpacesBookCreate201ResponseOneOf1Value -> result.value.status.value + } } override suspend fun reschedule(scope: MemberScope, bookingToken: String, startsAtISO: String, endsAtISO: String): MemberBooking { val (start, end) = interval(startsAtISO, endsAtISO) @@ -154,7 +157,7 @@ class GeneratedMemberActionsGateway(private val session: ApiSession,private val api(PersonalNotificationsApi::class.java, scope).apiV1NotificationsPreferencesRetrieve(scope.id).requireBody()) override suspend fun updatePreference(scope: MemberScope, feature: String, channel: String, enabled: Boolean, dailyDigest: Boolean?): List { val family = requireNotNull(Feature9d8Enum.entries.firstOrNull { it.value == feature }) { text(CoreStrings.CORE_THIS_NOTIFICATION_FAMILY_IS_UNAVAILABLE) } - val delivery = requireNotNull(Channel673Enum.entries.firstOrNull { it.value == channel }) { text(CoreStrings.CORE_THIS_NOTIFICATION_CHANNEL_IS_UNAVAILABLE) } + val delivery = requireNotNull(ChannelB6fEnum.entries.firstOrNull { it.value == channel }) { text(CoreStrings.CORE_THIS_NOTIFICATION_CHANNEL_IS_UNAVAILABLE) } require(dailyDigest == null || channel == "email") { text(CoreStrings.CORE_DAILY_DIGESTS_APPLY_TO_OPTIONAL_EMAIL_MESSAGES) } return preferences(api(PersonalNotificationsApi::class.java, scope).apiV1NotificationsPreferencesPartialUpdate(scope.id, PatchedPreferenceWrite(feature = family, channel = delivery, enabled = enabled, dailyDigest = dailyDigest)).requireBody()) diff --git a/app/src/main/java/org/spaceworks/app/actions/MakerCapabilitiesGateway.kt b/app/src/main/java/org/spaceworks/app/actions/MakerCapabilitiesGateway.kt index f3f36e2..da25d8e 100644 --- a/app/src/main/java/org/spaceworks/app/actions/MakerCapabilitiesGateway.kt +++ b/app/src/main/java/org/spaceworks/app/actions/MakerCapabilitiesGateway.kt @@ -13,8 +13,8 @@ import org.spaceworks.app.network.ApiSession import org.spaceworks.generated.api.TenantBootstrapApi /** Local capabilities projected from published bootstrap; never inferred from role names. */ -data class MakerCapabilities(val selfService:Boolean,val memberPreferences:Boolean,val dailyDigest:Boolean,val bookableSpaces:Boolean=false) { - companion object {val NONE=MakerCapabilities(false,false,false);val DEMO=MakerCapabilities(true,true,true,true)} +data class MakerCapabilities(val selfService:Boolean,val memberPreferences:Boolean,val dailyDigest:Boolean,val bookableSpaces:Boolean=false,val messaging:Boolean=false) { + companion object {val NONE=MakerCapabilities(false,false,false);val DEMO=MakerCapabilities(true,true,true,true,true)} } fun interface MakerCapabilitiesGateway {suspend fun load(scope:MemberScope):MakerCapabilities} class GeneratedMakerCapabilitiesGateway(private val session:ApiSession,private val text:MobileText=MobileText.ENGLISH):MakerCapabilitiesGateway { @@ -23,6 +23,7 @@ class GeneratedMakerCapabilitiesGateway(private val session:ApiSession,private v if(value.makerspace.id!=scope.id)throw ApiFailure(403,text(CoreStrings.CORE_YOUR_MAKERSPACE_ACCESS_CHANGED_CHOOSE_AGAIN),null,null) val accountModules=value.modules.containsAll(listOf("membership","member_accounts")) return MakerCapabilities(accountModules&&"membership.self_service" in value.features, - "notifications.member_preferences" in value.features,"notifications.digests" in value.features,"bookable_spaces" in value.modules) + "notifications.member_preferences" in value.features,"notifications.digests" in value.features,"bookable_spaces" in value.modules, + value.messaging.sms || value.messaging.whatsapp) } } diff --git a/app/src/main/java/org/spaceworks/app/auth/AccountStateHolder.kt b/app/src/main/java/org/spaceworks/app/auth/AccountStateHolder.kt index e1c80f2..d6f5d2b 100644 --- a/app/src/main/java/org/spaceworks/app/auth/AccountStateHolder.kt +++ b/app/src/main/java/org/spaceworks/app/auth/AccountStateHolder.kt @@ -15,9 +15,9 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.sync.Mutex import org.spaceworks.app.network.ApiFailure -enum class AccountRoute { SIGN_UP, SIGNUP_ACK, FORGOT_PASSWORD, RESET_PASSWORD, RESET_SUCCESS, RECOVERY_UNAVAILABLE, VERIFY_EMAIL } +enum class AccountRoute { SIGN_UP, SIGNUP_ACK, FORGOT_PASSWORD, RESET_PASSWORD, RESET_SUCCESS, RECOVERY_UNAVAILABLE, VERIFY_EMAIL, RESTORE_PHONE, RESTORE_CODE, RESTORE_SUCCESS } data class AccountState(val route:AccountRoute?=null,val email:String="",val busy:Boolean=false, - val error:String?=null,val notice:String?=null,val retryAtMillis:Long=0,val resendAtMillis:Long=0) + val error:String?=null,val notice:String?=null,val retryAtMillis:Long=0,val resendAtMillis:Long=0,val phone:String="") class AccountStateHolder(private val api:AuthGateway,private val signupAllowed:()->Boolean, private val onVerified:suspend()->Unit={},private val now:()->Long=System::currentTimeMillis,private val text:MobileText=MobileText.ENGLISH) { constructor(api:AuthGateway,signupAllowed:()->Boolean,onVerified:suspend()->Unit={},now:()->Long) @@ -28,11 +28,33 @@ class AccountStateHolder(private val api:AuthGateway,private val signupAllowed:( private class FlowGeneration {val mutex=Mutex()} @Volatile private var generation=FlowGeneration() /** Clear private context synchronously, even while an older request/callback is suspended. */ - fun clear(){val retry=state.value.retryAtMillis;generation=FlowGeneration();mutable.value=AccountState(retryAtMillis=retry)} + fun clear(){val retry=state.value.retryAtMillis;generation=FlowGeneration();restoreChallenge=null;mutable.value=AccountState(retryAtMillis=retry)} + private var restoreChallenge:PhoneChallenge?=null + /** Native restore of an archived phone-only account: code by SMS, then a new password. */ + suspend fun restoreStart(phone:String,resend:Boolean=false)=run {expected-> + if(resend && now() + if(!Regex("[0-9]{6}").matches(code))throw AuthMessage(text(CoreStrings.CORE_ENTER_THE_SIX_DIGIT_VERIFICATION_CODE)) + if(password.length<8)throw AuthMessage(text(CoreStrings.CORE_USE_AT_LEAST_8_CHARACTERS)) + if(password!=confirmation)throw AuthMessage(text(CoreStrings.CORE_PASSWORDS_DON_T_MATCH)) + val challenge=restoreChallenge?:throw AuthMessage(text(CoreStrings.CORE_YOUR_CODE_EXPIRED_SEND_A_NEW_CODE)) + val result=api.restorePhone(state.value.phone,code,password,challenge) + if(!accept(expected))return@run + restoreChallenge=null + if(!result.restored)throw AuthMessage(result.detail.ifBlank {text(CoreStrings.CORE_INVALID_OR_EXPIRED_VERIFICATION_CODE)}) + mutable.value=state.value.copy(route=AccountRoute.RESTORE_SUCCESS,notice=result.detail.takeIf {it.isNotBlank()}) + } private fun current(expected:FlowGeneration)=generation===expected private suspend fun accept(expected:FlowGeneration):Boolean {currentCoroutineContext().ensureActive();return current(expected)} fun open(route:AccountRoute,email:String="") {if(!state.value.busy)mutable.value=state.value.copy(route=route,email=email,error=null,notice=null)} - fun back(){if(!state.value.busy)mutable.value=state.value.copy(route=null,error=null,notice=null)} + fun back(){if(!state.value.busy){restoreChallenge=null;mutable.value=state.value.copy(route=null,error=null,notice=null)}} private fun email(value:String):String=value.trim().also {if(!it.contains('@') || it.contains(' '))throw AuthMessage(text(CoreStrings.CORE_ENTER_YOUR_EMAIL_ADDRESS))} suspend fun signUp(name:String,address:String,password:String,confirmation:String,phone:String)=run {expected-> if(!signupAllowed())throw AuthMessage(text(CoreStrings.CORE_ACCOUNT_REGISTRATION_IS_UNAVAILABLE_ON_THIS_SERVER)) diff --git a/app/src/main/java/org/spaceworks/app/auth/AuthBundle.kt b/app/src/main/java/org/spaceworks/app/auth/AuthBundle.kt index d1c75f3..fb16574 100644 --- a/app/src/main/java/org/spaceworks/app/auth/AuthBundle.kt +++ b/app/src/main/java/org/spaceworks/app/auth/AuthBundle.kt @@ -8,7 +8,8 @@ class AuthBundle(val holder:AuthStateHolder,val tokens:TokenStore,val demo:Boole val payments:org.spaceworks.app.payments.PaymentsStateHolder?=null, val staff:org.spaceworks.app.staff.StaffStateHolder?=null,val staffAvailable:()->Boolean={false}, val management:org.spaceworks.app.account.AccountManagementCoordinator?=null, - val deletionPreview:(List)->org.spaceworks.app.account.AccountDeletionPreview?={null}) { + val deletionPreview:(List)->org.spaceworks.app.account.AccountDeletionFlow?={null}, + val insights:org.spaceworks.app.insights.InsightsStateHolder?=null) { val previewGuidance=org.spaceworks.app.guidance.MemoryGuideStore() } @@ -19,6 +20,6 @@ fun AuthBundle.clearDemoState() { management?.forget(this) if(tokens.read()!=null)tokens.replace(null) holder.invalidateInstallation(tokens.snapshot().epoch) - accounts?.clear();staff?.resetAccount();member?.clear();actions?.clear();payments?.clear() + accounts?.clear();staff?.resetAccount();member?.clear();actions?.clear();payments?.clear();insights?.clear() links?.clear();push?.pause() } diff --git a/app/src/main/java/org/spaceworks/app/auth/AuthGateway.kt b/app/src/main/java/org/spaceworks/app/auth/AuthGateway.kt index 061cf52..534d53c 100644 --- a/app/src/main/java/org/spaceworks/app/auth/AuthGateway.kt +++ b/app/src/main/java/org/spaceworks/app/auth/AuthGateway.kt @@ -36,13 +36,43 @@ interface AuthGateway { suspend fun resendEmail():String {throw MobileCopyException(CoreStrings.CORE_EMAIL_VERIFICATION_IS_UNAVAILABLE_CONTACT_YOUR_MAKERSPACE)} val nativeSocialReady:Boolean get()=false suspend fun google(clientId:String,credential:suspend(String,String)->String):SocialLoginResponse {throw MobileCopyException(CoreStrings.CORE_DEVICE_SIGN_IN_IS_NOT_CONFIGURED_FOR_THIS_BUILD)} + /** Configured OIDC provider: [credential] receives the provider and server nonce and returns its ID token. */ + suspend fun oidc(provider:NativeOidc,credential:suspend(NativeOidc,String)->String):SocialLoginResponse {throw MobileCopyException(CoreStrings.CORE_DEVICE_SIGN_IN_IS_NOT_CONFIGURED_FOR_THIS_BUILD)} + /** Member-only phone sign-in. The returned challenge is reused, unchanged, by [phoneConfirm]. */ + suspend fun phoneStart(phone:String):PhoneChallenge {throw MobileCopyException(CoreStrings.CORE_PHONE_SIGN_IN_IS_UNAVAILABLE_ON_THIS_SERVER)} + suspend fun phoneConfirm(phone:String,code:String,challenge:PhoneChallenge):PhoneLoginResult {throw MobileCopyException(CoreStrings.CORE_PHONE_SIGN_IN_IS_UNAVAILABLE_ON_THIS_SERVER)} + /** Identity-only restore of an archived phone account; never returns a session. Reuses a [phoneStart] challenge. */ + suspend fun restorePhone(phone:String,code:String,newPassword:String,challenge:PhoneChallenge):AccountRestoreResult {throw MobileCopyException(CoreStrings.CORE_PHONE_SIGN_IN_IS_UNAVAILABLE_ON_THIS_SERVER)} + /** Revokes every browser session, device grant and push destination for this account on this server. */ + suspend fun logoutAll(expectedEpoch:Long):LogoutAllResponse {throw MobileCopyException(CoreStrings.CORE_COULDN_T_CONNECT_TRY_AGAIN)} } -fun interface AttestationProvider { suspend fun attest(challenge:String):JsonElement } +/** One attestation challenge bound to a phone sign-in attempt. Local state, never persisted or logged. */ +class PhoneChallenge(val challenge:String,val expiresInSeconds:Int) { override fun toString()="PhoneChallenge(redacted)" } +/** Produces the generated attestation union over one fresh server challenge. */ +fun interface AttestationProvider { suspend fun attest(challenge:String):DeviceAttestationPayload } class DevelopmentAttestation(private val debug:Boolean,private val emulator:Boolean,private val text:MobileText=MobileText.ENGLISH):AttestationProvider { val available get()=debug && emulator - override suspend fun attest(challenge:String):JsonElement { + override suspend fun attest(challenge:String):DeviceAttestationPayload { if(!debug || !emulator) throw AuthMessage(text(CoreStrings.CORE_DEVICE_VERIFICATION_IS_UNAVAILABLE_FOR_THIS_BUILD_CONTACT_YO)) - return buildJsonObject {put("type","development")} + // The schema's shared type enum only names play_integrity; development uses its open-object arm. + return DeviceAttestationPayload.JsonElementValue(buildJsonObject {put("type","development")}) + } +} +/** + * Debug emulators send the development proof that only a development server accepts. Every other + * build sends a Play Integrity Standard token over base64url(SHA-256(challenge)) when the owner + * supplied a Cloud project number; otherwise device sign-in stays visibly unavailable. + */ +class NativeAttestation(private val development:DevelopmentAttestation,private val integrity:(suspend(String)->String)?, + private val text:MobileText=MobileText.ENGLISH):AttestationProvider { + val available get()=development.available || integrity!=null + override suspend fun attest(challenge:String):DeviceAttestationPayload { + if(development.available)return development.attest(challenge) + val provider=integrity?:throw AuthMessage(text(CoreStrings.CORE_DEVICE_VERIFICATION_IS_UNAVAILABLE_FOR_THIS_BUILD_CONTACT_YO)) + val token=try {provider(challenge)}catch(cancelled:kotlinx.coroutines.CancellationException){throw cancelled} + catch(_:Exception){throw AuthMessage(text(CoreStrings.CORE_THIS_DEVICE_COULD_NOT_VERIFY_THE_APP_TRY_AGAIN))} + return DeviceAttestationPayload.DeviceAttestationPayloadAnyOfValue( + DeviceAttestationPayloadAnyOf(DeviceAttestationPayloadTypeEnum.play_integrity,token)) } } class GeneratedAuthGateway(private val session:ApiSession,private val appId:String, @@ -101,9 +131,44 @@ class GeneratedAuthGateway(private val session:ApiSession,private val appId:Stri override suspend fun signUp(name:String,email:String,password:String,phone:String)=session.authPublic.apiV1AuthMemberSignUpCreate(MemberSignUp(name,email,password,phone,"")).requireBody().detail override suspend fun confirmEmail(code:String)=session.globalApi(org.spaceworks.generated.api.AuthApi::class.java).apiV1AuthEmailVerificationConfirmCreate(EmailVerificationConfirm(code)).requireBody().detail override suspend fun resendEmail()=session.globalApi(org.spaceworks.generated.api.AuthApi::class.java).apiV1AuthEmailVerificationResendCreate().requireBody().detail - override val nativeSocialReady get()=(attestation as? DevelopmentAttestation)?.available==true + override val nativeSocialReady get()=when(attestation){is NativeAttestation->attestation.available;is DevelopmentAttestation->attestation.available;else->true} + private suspend fun challenge()=session.devicePublic.apiV1AuthDeviceAttestationChallengeCreate(DeviceIdentity(PlatformEnum.android,appId,environment)).requireBody() + override suspend fun oidc(provider:NativeOidc,credential:suspend(NativeOidc,String)->String):SocialLoginResponse { + val slug=provider.key.removePrefix("oidc:").takeIf {provider.key.startsWith("oidc:") && it.matches(Regex("[A-Za-z0-9_-]{1,64}"))} + ?:throw MobileCopyException(CoreStrings.CORE_DEVICE_SIGN_IN_IS_NOT_CONFIGURED_FOR_THIS_BUILD) + val challenge=challenge() + val nonce=session.socialPublic.apiV1AuthSocialNonceCreate(SocialNonce(provider.key,Surface356Enum.member,DeliveryEnum.device,ClientPlatformEnum.android,challenge.challenge)).requireBody() + val idToken=credential(provider,nonce.nonce) + val proof=attestation.attest(challenge.challenge) + return session.socialPublic.apiV1AuthSocialOidcCreate(slug,SocialLogin(idToken,nonce.nonce,Surface356Enum.member,DeliveryEnum.device,ClientPlatformEnum.android,challenge=challenge.challenge,attestation=proof)).requireBody() + } + override suspend fun phoneStart(phone:String):PhoneChallenge { + val challenge=challenge() + session.authPublic.apiV1AuthPhoneLoginStartCreate(PhoneLoginStart(phone,DeliveryEnum.device,ClientPlatformEnum.android,challenge.challenge)).requireBody() + return PhoneChallenge(challenge.challenge,challenge.expiresIn) + } + override suspend fun restorePhone(phone:String,code:String,newPassword:String,challenge:PhoneChallenge):AccountRestoreResult { + val proof=attestation.attest(challenge.challenge) + return session.publicApi(org.spaceworks.generated.api.AccountsApi::class.java).apiV1AuthAccountRestorePhoneCreate( + AccountRestorePhone(phone,code,newPassword,DeliveryEnum.device,ClientPlatformEnum.android,challenge.challenge,proof)).requireBody() + } + override suspend fun phoneConfirm(phone:String,code:String,challenge:PhoneChallenge):PhoneLoginResult { + val proof=attestation.attest(challenge.challenge) + return session.authPublic.apiV1AuthPhoneLoginConfirmCreate(PhoneLoginConfirm(phone,code,DeliveryEnum.device,ClientPlatformEnum.android,challenge.challenge,proof)).requireBody() + } + override suspend fun logoutAll(expectedEpoch:Long):LogoutAllResponse=withContext(kotlinx.coroutines.NonCancellable) { + if(session.installationEpoch!=expectedEpoch)throw kotlinx.coroutines.CancellationException("Session changed") + // Remove this device's push registrations while the grant still authenticates them. + try {onBeforeOwnedSignOut(expectedEpoch)}catch(_:Exception){} + if(session.installationEpoch!=expectedEpoch)throw kotlinx.coroutines.CancellationException("Session changed") + val result=session.pinnedGlobalApi(org.spaceworks.generated.api.AuthApi::class.java,expectedEpoch) {call-> + if(session.installationEpoch!=expectedEpoch)call.cancel() + }.apiV1AuthLogoutAllCreate().requireBody() + if(session.installationEpoch==expectedEpoch)onNativeSignOut() + result + } override suspend fun google(clientId:String,credential:suspend(String,String)->String):SocialLoginResponse { - val challenge=session.devicePublic.apiV1AuthDeviceAttestationChallengeCreate(DeviceIdentity(PlatformEnum.android,appId,environment)).requireBody() + val challenge=challenge() val nonce=session.socialPublic.apiV1AuthSocialNonceCreate(SocialNonce("google",Surface356Enum.member,DeliveryEnum.device,ClientPlatformEnum.android,challenge.challenge)).requireBody() val idToken=credential(clientId,nonce.nonce) val proof=attestation.attest(challenge.challenge) diff --git a/app/src/main/java/org/spaceworks/app/auth/AuthOperationOutcome.kt b/app/src/main/java/org/spaceworks/app/auth/AuthOperationOutcome.kt index 63c3cae..24cc1a4 100644 --- a/app/src/main/java/org/spaceworks/app/auth/AuthOperationOutcome.kt +++ b/app/src/main/java/org/spaceworks/app/auth/AuthOperationOutcome.kt @@ -13,6 +13,7 @@ internal fun authOperationKind(before:AuthState,after:AuthState,accepted:Boolean after.user!=null && before.user==null && accepted -> if(after.user.mustChangePassword)AuthOperationKind.PROGRESSED else AuthOperationKind.ACCEPTED after.mfa!=null && before.mfa==null -> AuthOperationKind.PROGRESSED + after.phone?.codeSent==true && before.phone?.codeSent!=true -> AuthOperationKind.PROGRESSED after.recoveryCodes.isNotEmpty() && before.recoveryCodes.isEmpty() -> AuthOperationKind.PROGRESSED else -> null } diff --git a/app/src/main/java/org/spaceworks/app/auth/AuthStateHolder.kt b/app/src/main/java/org/spaceworks/app/auth/AuthStateHolder.kt index d77a7c1..38df275 100644 --- a/app/src/main/java/org/spaceworks/app/auth/AuthStateHolder.kt +++ b/app/src/main/java/org/spaceworks/app/auth/AuthStateHolder.kt @@ -16,23 +16,42 @@ import org.spaceworks.generated.model.* class AuthMessage(message:String):Exception(message) class AuthCancelled:Exception() -enum class AuthGate { WEB_CONSOLE, SESSION_ENDED } +enum class AuthGate { WEB_CONSOLE, SESSION_ENDED, UPDATE_REQUIRED } +/** Local phone sign-in progress. The attestation challenge stays private to the holder. */ +data class PhoneSignIn(val phone:String="",val codeSent:Boolean=false,val resendAtMillis:Long=0,val expiresAtMillis:Long=0) +/** Compares dotted numeric versions component by component; unparsable values never block the app. */ +object AppVersion { + private fun parts(value:String?)=value?.trim()?.takeIf {Regex("\\d+(\\.\\d+)*").matches(it)}?.split('.')?.map {it.toBigInteger()} + fun below(current:String?,minimum:String?):Boolean { + val have=parts(current)?:return false + val need=parts(minimum)?:return false + for(i in 0 until maxOf(have.size,need.size)) { + val a=have.getOrElse(i){java.math.BigInteger.ZERO};val b=need.getOrElse(i){java.math.BigInteger.ZERO} + if(a!=b)return a = emptyList(), val error:String?=null,val notice:String?=null,val retryAtMillis:Long=0, - val mfaExpiresAtMillis:Long=0,val expiredMfa:Boolean=false,val gate:AuthGate?=null) { + val mfaExpiresAtMillis:Long=0,val expiredMfa:Boolean=false,val gate:AuthGate?=null,val phone:PhoneSignIn?=null) { val memberships get()=user?.makerspaces.orEmpty().filter {it.source==AuthMembershipSourceEnum.membership} val selected get()=memberships.firstOrNull {it.id==selectedId} - val passwordEnabled get()=(config?.passwordLogin?.get("enabled") as? JsonPrimitive)?.booleanOrNull != false && - (config?.memberAccounts?.get("enabled") as? JsonPrimitive)?.booleanOrNull != false - // The owner contract has no native session_authority yet. Role/action text alone cannot grant native staff tools. - val staffAllowed get()=false + /** Native deployments publish their enabled methods; legacy servers keep the browser-era flags. */ + val native get()=config?.native + val passwordEnabled get()=native?.providers?.password ?: ((config?.passwordLogin?.get("enabled") as? JsonPrimitive)?.booleanOrNull != false && + (config?.memberAccounts?.get("enabled") as? JsonPrimitive)?.booleanOrNull != false) + val phoneEnabled get()=native?.providers?.phone==true + /** Staff tools need a native staff grant; membership actions alone never grant them. */ + val staffAllowed get()=user?.sessionAuthority=="native_staff" val selfRegistrationEnabled get()=config!=null && (config?.memberAccounts?.get("enabled") as? JsonPrimitive)?.booleanOrNull!=false && (config?.selfRegistration?.get("enabled") as? JsonPrimitive)?.booleanOrNull!=false val googleClientId get():String? { + native?.let {return it.providers.google?.androidClientId?.takeIf {id->id.isNotBlank()}} if((config?.memberAccounts?.get("enabled") as? JsonPrimitive)?.booleanOrNull==false)return null val google=config?.socialAuth?.get("google") as? JsonObject ?: return null if((google["enabled"] as? JsonPrimitive)?.booleanOrNull!=true)return null @@ -42,7 +61,7 @@ data class AuthState(val busy:Boolean=false,val initialized:Boolean=false,val co class AuthStateHolder(private val api:AuthGateway,private val tokens:TokenStore, private val readSelection:()->Int?={null},private val saveSelection:(Int?)->Unit={}, private val now:()->Long=System::currentTimeMillis,private val onSessionEnded:()->Unit={}, - private val onScopeChanged:()->Unit={},private val text:MobileText=MobileText.ENGLISH) { + private val onScopeChanged:()->Unit={},private val text:MobileText=MobileText.ENGLISH,private val appVersion:String?=null) { constructor(api:AuthGateway,tokens:TokenStore,readSelection:()->Int?={null},saveSelection:(Int?)->Unit={},now:()->Long=System::currentTimeMillis,onSessionEnded:()->Unit) :this(api,tokens,readSelection,saveSelection,now,onSessionEnded,{},MobileText.ENGLISH) @@ -72,7 +91,12 @@ class AuthStateHolder(private val api:AuthGateway,private val tokens:TokenStore, val nativeSocialReady get()=api.nativeSocialReady suspend fun initialize()=runOperation { val config=api.config() - mutable.value=state.value.copy(config=config,initialized=true) + if(AppVersion.below(appVersion,config.native?.minAppVersion?.android)) { + // Keep any stored session: it resumes after the update. Nothing else is requested. + mutable.value=state.value.copy(config=config,initialized=true,gate=AuthGate.UPDATE_REQUIRED) + return@runOperation + } + mutable.value=state.value.copy(config=config,initialized=true,gate=state.value.gate.takeUnless {it==AuthGate.UPDATE_REQUIRED}) val restored=withContext(Dispatchers.IO){tokens.snapshot()} if(restored.pair!=null){installedEpoch=restored.epoch;refreshIdentity()} } @@ -104,6 +128,61 @@ class AuthStateHolder(private val api:AuthGateway,private val tokens:TokenStore, ensure(!result.refresh.isNullOrBlank() && validGrant(result.deviceGrant),text(CoreStrings.CORE_THIS_SERVER_DID_NOT_RETURN_A_DEVICE_SESSION_CONTACT_YOUR_MAK)) acceptPair(TokenPair(result.access,requireNotNull(result.refresh))) } + private var phoneChallenge:PhoneChallenge?=null + fun openPhone() {if(!state.value.busy && state.value.phoneEnabled){phoneChallenge=null;mutable.value=state.value.copy(phone=PhoneSignIn(),error=null,notice=null,gate=null)}} + fun changePhoneNumber() {if(!state.value.busy){phoneChallenge=null;mutable.value=state.value.copy(phone=PhoneSignIn(state.value.phone?.phone.orEmpty()),error=null)}} + fun closePhone() {if(!state.value.busy){phoneChallenge=null;mutable.value=state.value.copy(phone=null,error=null)}} + suspend fun sendPhoneCode(phone:String,onOutcome:((AuthOperationOutcome)->Unit)?=null)=runOperation(onOutcome=onOutcome) { + val number=phone.filter {it.isDigit() || it=='+'} + ensure(Regex("\\+[1-9]\\d{6,14}").matches(number),text(CoreStrings.CORE_ENTER_YOUR_PHONE_NUMBER_WITH_ITS_COUNTRY_CODE)) + ensure(state.value.phoneEnabled,text(CoreStrings.CORE_PHONE_SIGN_IN_IS_UNAVAILABLE_ON_THIS_SERVER)) + ensure(api.nativeSocialReady,text(CoreStrings.CORE_DEVICE_SIGN_IN_IS_NOT_CONFIGURED_FOR_THIS_BUILD_CONTACT_YOUR)) + val challenge=api.phoneStart(number) + ensureOperationCurrent() + phoneChallenge=challenge + val sent=now() + mutable.value=state.value.copy(phone=PhoneSignIn(number,true,sent+PHONE_RESEND_MILLIS,sent+challenge.expiresInSeconds.coerceAtLeast(0)*1000L)) + } + suspend fun verifyPhoneCode(code:String,onOutcome:((AuthOperationOutcome)->Unit)?=null)=runOperation(onOutcome=onOutcome) { + val phone=state.value.phone?.takeIf {it.codeSent} ?: throw AuthMessage(text(CoreStrings.CORE_RESTART_SIGN_IN_AND_TRY_AGAIN)) + ensure(Regex("\\d{6}").matches(code.trim()),text(CoreStrings.CORE_ENTER_THE_SIX_DIGIT_CODE)) + val challenge=phoneChallenge + if(challenge==null || now()>=phone.expiresAtMillis) { + phoneChallenge=null + mutable.value=state.value.copy(phone=phone.copy(codeSent=false,resendAtMillis=0)) + throw AuthMessage(text(CoreStrings.CORE_YOUR_CODE_EXPIRED_SEND_A_NEW_CODE)) + } + val result=api.phoneConfirm(phone.phone,code.trim(),challenge) + ensureOperationCurrent() + phoneChallenge=null + when(result) { + is PhoneLoginResult.DeviceTokenResponseValue -> { + mutable.value=state.value.copy(phone=null) + acceptPair(TokenPair(result.value.access,result.value.refresh)) + } + is PhoneLoginResult.MFACompletedValue -> { + ensure(result.value.surface!="verification_only" && !result.value.refresh.isNullOrBlank() && validGrant(result.value.deviceGrant), + text(CoreStrings.CORE_THIS_SERVER_DID_NOT_RETURN_A_DEVICE_SESSION_CONTACT_YOUR_MAK)) + mutable.value=state.value.copy(phone=null) + acceptPair(TokenPair(result.value.access,requireNotNull(result.value.refresh))) + } + is PhoneLoginResult.MFAChallengeResponseValue -> { + ensure(result.value.mfaRequired,text(CoreStrings.CORE_RESTART_SIGN_IN_AND_TRY_AGAIN)) + challengeExpiresAt=now()+result.value.expiresIn.coerceAtLeast(0)*1000L + mutable.value=state.value.copy(phone=null,mfa=result.value,enrollment=null,error=null,mfaExpiresAtMillis=challengeExpiresAt,expiredMfa=false) + } + } + } + val oidcProviders get()=state.value.native?.providers?.oidc.orEmpty().filter {it.key.startsWith("oidc:") && it.clientId.isNotBlank() && it.issuer.startsWith("https://")} + suspend fun oidcSignIn(provider:NativeOidc,credential:suspend(NativeOidc,String)->String,onOutcome:((AuthOperationOutcome)->Unit)?=null)=runOperation(onOutcome=onOutcome) { + mutable.value=state.value.copy(gate=null) + ensure(oidcProviders.any {it.key==provider.key},text(CoreStrings.CORE_THIS_SIGN_IN_PROVIDER_IS_UNAVAILABLE_TRY_AGAIN)) + ensure(api.nativeSocialReady,text(CoreStrings.CORE_DEVICE_SIGN_IN_IS_NOT_CONFIGURED_FOR_THIS_BUILD_CONTACT_YOUR)) + val result=try {api.oidc(provider,credential)}catch(f:org.spaceworks.app.platform.PlatformUnavailable){throw AuthMessage(text(CoreStrings.CORE_THIS_SIGN_IN_PROVIDER_IS_UNAVAILABLE_TRY_AGAIN))} + ensureOperationCurrent() + ensure(!result.refresh.isNullOrBlank() && validGrant(result.deviceGrant),text(CoreStrings.CORE_THIS_SERVER_DID_NOT_RETURN_A_DEVICE_SESSION_CONTACT_YOUR_MAK)) + acceptPair(TokenPair(result.access,requireNotNull(result.refresh))) + } suspend fun enroll()=runOperation { val challenge=requireChallenge() val enrollment=api.enroll(challenge.challenge) @@ -171,7 +250,17 @@ class AuthStateHolder(private val api:AuthGateway,private val tokens:TokenStore, if(state.value.selectedId!=id)onScopeChanged() saveSelection(id);mutable.value=state.value.copy(selectedId=id) } - fun dismissGate() {if(!state.value.busy)mutable.value=state.value.copy(gate=null,error=null)} + fun dismissGate() {if(!state.value.busy && state.value.gate!=AuthGate.UPDATE_REQUIRED)mutable.value=state.value.copy(gate=null,error=null)} + /** Server-confirmed revocation of every session; a failed request keeps this device signed in. */ + suspend fun signOutEverywhere()=runOperation(ignoreThrottle=true) { + val expected=acceptedInstallationEpoch?:throw AuthMessage(text(CoreStrings.CORE_PLEASE_SIGN_IN_AGAIN)) + api.logoutAll(expected) + acceptedIdentityEpoch=null + onSessionEnded() + if(tokens.snapshot().epoch==expected)saveSelection(null) + mutable.value=AuthState(initialized=true,config=state.value.config,notice=text(CoreStrings.CORE_SIGNED_OUT_ON_EVERY_DEVICE_AND_BROWSER),retryAtMillis=retryAt) + clearInstalled(expected) + } suspend fun signOut()=runOperation(ignoreThrottle=true) { val expected=installedEpoch val installed=tokens.snapshot() @@ -357,6 +446,7 @@ class AuthStateHolder(private val api:AuthGateway,private val tokens:TokenStore, acceptedIdentityEpoch=expected mutable.value=state.value.copy(user=user,selectedId=selected,mfa=null,enrollment=null,error=null) } + private companion object { const val PHONE_RESEND_MILLIS=30_000L } private fun requireChallenge():MFAChallengeResponse { if(now()>=challengeExpiresAt){clearExpiredChallenge();throw AuthMessage(text(CoreStrings.CORE_YOUR_VERIFICATION_SESSION_EXPIRED_SIGN_IN_TO_REQUEST_A_NEW_O))} return state.value.mfa ?: throw AuthMessage(text(CoreStrings.CORE_RESTART_SIGN_IN_AND_TRY_AGAIN)) diff --git a/app/src/main/java/org/spaceworks/app/copy/CoreStrings.kt b/app/src/main/java/org/spaceworks/app/copy/CoreStrings.kt index 03cdedc..1e56a5e 100644 --- a/app/src/main/java/org/spaceworks/app/copy/CoreStrings.kt +++ b/app/src/main/java/org/spaceworks/app/copy/CoreStrings.kt @@ -133,4 +133,11 @@ object CoreStrings { val CORE_THIS_CHANGED_REFRESH_AND_TRY_AGAIN=MobileCopyKey(R.string.core_this_changed_refresh_and_try_again,"This changed. Refresh and try again.") val CORE_TOO_MANY_REQUESTS_TRY_AGAIN_LATER=MobileCopyKey(R.string.core_too_many_requests_try_again_later,"Too many requests. Try again later.") val CORE_THE_SERVER_IS_UNAVAILABLE_TRY_AGAIN_LATER=MobileCopyKey(R.string.core_the_server_is_unavailable_try_again_later,"The server is unavailable. Try again later.") + val CORE_PHONE_SIGN_IN_IS_UNAVAILABLE_ON_THIS_SERVER=MobileCopyKey(R.string.core_phone_sign_in_is_unavailable_on_this_server,"Phone sign-in is unavailable on this server.") + val CORE_THIS_DEVICE_COULD_NOT_VERIFY_THE_APP_TRY_AGAIN=MobileCopyKey(R.string.core_this_device_could_not_verify_the_app_try_again,"This device could not verify the app. Try again.") + val CORE_ENTER_YOUR_PHONE_NUMBER_WITH_ITS_COUNTRY_CODE=MobileCopyKey(R.string.core_enter_your_phone_number_with_its_country_code,"Enter your phone number with its country code.") + val CORE_ENTER_THE_SIX_DIGIT_CODE=MobileCopyKey(R.string.core_enter_the_six_digit_code,"Enter the six-digit code.") + val CORE_YOUR_CODE_EXPIRED_SEND_A_NEW_CODE=MobileCopyKey(R.string.core_your_code_expired_send_a_new_code,"Your code expired. Send a new code.") + val CORE_SIGNED_OUT_ON_EVERY_DEVICE_AND_BROWSER=MobileCopyKey(R.string.core_signed_out_on_every_device_and_browser,"Signed out on every device and browser.") + val CORE_THIS_SIGN_IN_PROVIDER_IS_UNAVAILABLE_TRY_AGAIN=MobileCopyKey(R.string.core_this_sign_in_provider_is_unavailable_try_again,"This sign-in provider is unavailable. Try again.") } diff --git a/app/src/main/java/org/spaceworks/app/insights/Insights.kt b/app/src/main/java/org/spaceworks/app/insights/Insights.kt new file mode 100644 index 0000000..3441f45 --- /dev/null +++ b/app/src/main/java/org/spaceworks/app/insights/Insights.kt @@ -0,0 +1,162 @@ +package org.spaceworks.app.insights + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.contentOrNull +import org.spaceworks.app.auth.requireBody +import org.spaceworks.app.member.MemberLoad +import org.spaceworks.app.member.MemberScope +import org.spaceworks.app.network.ApiFailure +import org.spaceworks.app.network.ApiSession +import org.spaceworks.app.network.retryDeadlineMillis +import org.spaceworks.generated.api.DashboardApi +import org.spaceworks.generated.api.MemberActivityApi +import org.spaceworks.generated.api.MessagingApi +import org.spaceworks.generated.model.Channel06eEnum +import org.spaceworks.generated.model.ConsentWrite +import org.spaceworks.generated.model.ConsentWriteActionEnum +import org.spaceworks.generated.model.MemberUsageSeries +import org.spaceworks.generated.model.MessagingChannel + +/** One labelled point of a server series; [value] is the server's number as text, never recomputed. */ +data class UsagePoint(val label:String,val value:String,val amount:Double) +/** A server series reduced to its x label and first declared measure. Empty points means table-only. */ +data class UsageSeries(val points:List,val unit:String,val summary:String) +data class LoanUsage(val count:Int,val returned:Int,val currentlyOut:Int,val monthly:UsageSeries) +data class MachineUsage(val hours:String,val monthly:UsageSeries,val top:UsageSeries) +data class CountUsage(val count:Int,val monthly:UsageSeries) +data class PaymentUsage(val paidByCurrency:Map,val outstandingByCurrency:Map) +/** Absent sections are gated or unavailable; they are never shown as zero usage. */ +data class UsageSummary(val available:Boolean,val loans:LoanUsage?,val machines:MachineUsage?,val bookings:CountUsage?, + val eventsAttended:Int?,val payments:PaymentUsage?) + +enum class StaffScope { FULL, MACHINE, HANDOVER } +data class StaffCounter(val key:String,val value:Int) +data class StaffWidget(val key:String,val title:String,val kind:String,val headline:String?) +data class StaffDashboard(val scope:StaffScope,val counters:List,val queue:List, + val widgets:List,val outstandingByCurrency:Map) + +data class ConsentChannel(val available:Boolean,val consented:Boolean,val phoneVerified:Boolean,val phoneMasked:String,val suppressed:Boolean) { + val canOptIn get()=available && phoneVerified && !suppressed && !consented +} +data class MessagingConsent(val sms:ConsentChannel,val whatsapp:ConsentChannel) + +interface InsightsGateway { + suspend fun usage(scope:MemberScope):UsageSummary + suspend fun dashboard(scope:MemberScope):StaffDashboard + suspend fun messaging(scope:MemberScope):MessagingConsent + suspend fun consent(scope:MemberScope,channel:String,optIn:Boolean):MessagingConsent +} + +/** Generated operations only; every call carries the selected makerspace header. */ +class GeneratedInsightsGateway(private val session:ApiSession):InsightsGateway { + private fun api(type:Class,scope:MemberScope)=session.scopedApi(type,scope.id) + override suspend fun usage(scope:MemberScope):UsageSummary { + val value=api(MemberActivityApi::class.java,scope).apiV1MemberMakerspacesMyUsageRetrieve(scope.id).requireBody() + return UsageSummary(value.usageAvailable, + value.loans?.let {LoanUsage(it.count,it.returnedCount,it.currentlyOut,series(it.monthly))}, + value.machines?.let {MachineUsage(it.hours,series(it.monthly),series(it.topMachines))}, + value.bookings?.let {CountUsage(it.count,series(it.monthly))}, + value.events?.attendedCount, + value.payments?.let {PaymentUsage(it.paidByCurrency,it.outstandingByCurrency)}) + } + override suspend fun dashboard(scope:MemberScope):StaffDashboard { + val value=api(DashboardApi::class.java,scope).apiV1AdminMakerspaceDashboardRetrieve(scope.id).requireBody() + // The schema declares default 0 for every counter, so an omitted (unavailable) counter cannot be + // told apart from zero. Only server-reported non-zero counters are shown. + val counters=listOf("pending_requests" to value.pendingRequests,"awaiting_issue" to value.awaitingIssue,"overdue_loans" to value.overdueLoans, + "pending_bookings" to value.pendingBookings,"upcoming_bookings" to value.upcomingBookings,"upcoming_events" to value.upcomingEvents, + "pending_event_registrations" to value.pendingEventRegistrations,"open_problem_reports" to value.openProblemReports,"low_stock" to value.lowStock, + "pending_prints" to value.pendingPrints,"active_prints" to value.activePrints,"prints_awaiting_collection" to value.printsAwaitingCollection, + "maintenance_overdue" to value.maintenanceOverdue,"warranty_expiring" to value.warrantyExpiring, + "stocktakes_awaiting_approval" to value.stocktakesAwaitingApproval,"pending_payments" to value.pendingPayments,"failed_emails" to value.failedEmails) + .mapNotNull {(key,count)->count?.takeIf {it>0}?.let {StaffCounter(key,it)}} + val queue=value.queueTiles?.let {tiles->listOfNotNull(tiles.readyToIssue?.let {StaffCounter("ready_to_issue",it)}, + tiles.dueReturns?.let {StaffCounter("due_returns",it)},tiles.pickups?.let {StaffCounter("pickups",it)})}.orEmpty() + val widgets=value.widgets.orEmpty().map {StaffWidget(it.key,it.title,it.kind.value,headline(it.`data`))} + return StaffDashboard(when(value.scopeMode.value){"handover"->StaffScope.HANDOVER;"machine"->StaffScope.MACHINE;else->StaffScope.FULL}, + counters,queue,widgets,value.outstandingByCurrency.orEmpty()) + } + override suspend fun messaging(scope:MemberScope)=consent(api(MessagingApi::class.java,scope).apiV1MeMessagingRetrieve(scope.id).requireBody()) + override suspend fun consent(scope:MemberScope,channel:String,optIn:Boolean):MessagingConsent { + val wire=requireNotNull(Channel06eEnum.entries.firstOrNull {it.value==channel}) + val action=if(optIn)ConsentWriteActionEnum.opted_in else ConsentWriteActionEnum.opted_out + return consent(api(MessagingApi::class.java,scope).apiV1MeMessagingConsentCreate(ConsentWrite(scope.id,wire,action)).requireBody()) + } + private fun consent(value:org.spaceworks.generated.model.MemberMessaging)=MessagingConsent(channel(value.sms),channel(value.whatsapp)) + private fun channel(value:MessagingChannel)=ConsentChannel(value.available,value.consented,value.phoneVerified,value.phoneMasked,value.suppressed) + companion object { + private fun text(value:JsonElement?)=(value as? JsonPrimitive)?.contentOrNull + /** Uses only the series' declared x field and first measure; rows without them are skipped. */ + fun series(value:MemberUsageSeries):UsageSeries { + val x=value.chartSpec.x.`field` + val measure=value.chartSpec.propertyValues.firstOrNull() + val points=if(measure==null)emptyList() else value.rows.mapNotNull {row-> + val label=text(row[x])?:return@mapNotNull null + val raw=text(row[measure.`field`])?:return@mapNotNull null + raw.toDoubleOrNull()?.let {UsagePoint(label,raw,it)} + } + return UsageSeries(points,measure?.unit.orEmpty(),value.chartSpec.altTemplate) + } + /** A count/queue widget's single headline number, when the report object exposes one. */ + fun headline(data:Map):String? = + listOf("count","total","value").firstNotNullOfOrNull {key->text(data[key])?.takeIf {it.toDoubleOrNull()!=null}} + } +} + +data class InsightsState(val scope:MemberScope?=null,val usage:MemberLoad = MemberLoad(), + val dashboard:MemberLoad = MemberLoad(),val messaging:MemberLoad = MemberLoad(), + val consentBusy:String?=null,val consentError:String?=null,val retryAtMillis:Long=0) + +/** Per-session, per-makerspace insight state. Scope changes and sign-out discard everything cached. */ +class InsightsStateHolder(private val api:InsightsGateway,private val now:()->Long=System::currentTimeMillis, + private val failureText:(ApiFailure)->String={it.detail},private val unavailableText:()->String={"Couldn't connect. Try again."}, + private val onUnauthorized:suspend()->Unit={}) { + private val mutable=MutableStateFlow(InsightsState());val state=mutable.asStateFlow() + private var generation=0L + fun activate(scope:MemberScope){if(state.value.scope!=scope){generation++;mutable.value=InsightsState(scope=scope,retryAtMillis=state.value.retryAtMillis)}} + fun clear(){generation++;mutable.value=InsightsState()} + private suspend fun load(read:(InsightsState)->MemberLoad,write:(InsightsState,MemberLoad)->InsightsState,block:suspend(MemberScope)->T) { + val scope=state.value.scope?:return + if(read(state.value).loading || now()s.copy(usage=v)}) {api.usage(it)} + suspend fun refreshDashboard()=load({it.dashboard},{s,v->s.copy(dashboard=v)}) {api.dashboard(it)} + suspend fun refreshMessaging()=load({it.messaging},{s,v->s.copy(messaging=v)}) {api.messaging(it)} + /** The server decides consent; the switch shows its returned state, and any refusal refetches it. */ + suspend fun setConsent(channel:String,optIn:Boolean) { + val scope=state.value.scope?:return + if(state.value.consentBusy!=null || now()>) { + if(stats.isEmpty())return + val scale=androidx.compose.ui.platform.LocalDensity.current.fontScale.coerceAtLeast(1f) + BoxWithConstraints(Modifier.fillMaxWidth()) { + val columns=(maxWidth/(104.dp*scale)).toInt().coerceIn(1,minOf(3,stats.size)) + Column(verticalArrangement=Arrangement.spacedBy(12.dp)) { + stats.chunked(columns).forEach {row-> + Row(Modifier.fillMaxWidth().height(IntrinsicSize.Min),horizontalArrangement=Arrangement.spacedBy(12.dp)) { + row.forEach {(label,value)->StatCard(label,value,Modifier.weight(1f).fillMaxHeight())} + repeat(columns-row.size) {Spacer(Modifier.weight(1f))} + } + } + } + } +} +/** Status word on a soft pill: the word carries the meaning, colour only reinforces it. */ +@Composable private fun StatusPill(word:String,on:Boolean) { + Surface(shape=RoundedCornerShape(50),color=if(on)MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surfaceContainerHighest) { + Text(word,Modifier.heightIn(min=32.dp).padding(horizontal=14.dp,vertical=6.dp),style=MaterialTheme.typography.labelLarge, + color=if(on)MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurfaceVariant) + } +} +/** Labelled bars with each value written out; the bar itself is decorative. No totals across units. */ +@Composable private fun SeriesBars(title:String,series:UsageSeries) { + val copyText=rememberMobileText() + Text(title,style=MaterialTheme.typography.titleMedium,modifier=Modifier.semantics {heading()}) + if(series.points.isEmpty()){Text(copyText(InsightsStrings.INSIGHTS_NO_ACTIVITY_YET),color=MaterialTheme.colorScheme.onSurfaceVariant);return} + val max=series.points.maxOf {it.amount}.takeIf {it>0}?:1.0 + Column(verticalArrangement=Arrangement.spacedBy(10.dp)) { + series.points.forEach {point-> + val description=copyText(InsightsStrings.INSIGHTS_POINT_VALUE,point.label,point.value,series.unit).trim() + Column(Modifier.fillMaxWidth().semantics(mergeDescendants=true) {contentDescription=description},verticalArrangement=Arrangement.spacedBy(4.dp)) { + Row(Modifier.fillMaxWidth(),horizontalArrangement=Arrangement.SpaceBetween) { + Text(point.label,style=MaterialTheme.typography.bodyMedium,modifier=Modifier.weight(1f)) + Text(if(series.unit.isBlank())point.value else "${point.value} ${series.unit}",style=MaterialTheme.typography.bodyMedium.copy(fontWeight=FontWeight.SemiBold,fontFeatureSettings="tnum")) + } + Box(Modifier.fillMaxWidth().height(8.dp).background(MaterialTheme.colorScheme.primaryContainer,RoundedCornerShape(4.dp))) { + Box(Modifier.fillMaxWidth((point.amount.coerceAtLeast(0.0)/max).toFloat().coerceIn(0f,1f)).fillMaxHeight() + // Accent ink reads on the soft accent track in both themes (dark accent alone is under 3:1). + .background(MaterialTheme.colorScheme.onPrimaryContainer,RoundedCornerShape(4.dp))) + } + } + } + } +} +@Composable private fun Money(values:Map):String { + val copyText=rememberMobileText() + // Each currency stays separate; amounts are never added across currencies. + return values.entries.joinToString("\n") {(currency,amount)->copyText(InsightsStrings.INSIGHTS_MONEY_VALUE,currency.uppercase(),amount)} +} + +/** Member Home: the caller's own usage. Missing sections are omitted, never shown as zero. */ +@Composable fun MemberUsagePanel(holder:InsightsStateHolder) { + val copyText=rememberMobileText() + val s by holder.state.collectAsStateWithLifecycle() + val scope=rememberCoroutineScope() + LaunchedEffect(holder,s.scope) {if(s.scope!=null && s.usage.value==null && !s.usage.loading && s.usage.error==null)holder.refreshUsage()} + MemberSection(copyText(InsightsStrings.INSIGHTS_YOUR_ACTIVITY)) { + MemberLoaded(s.usage,{scope.launch {holder.refreshUsage()}}) {usage-> + Column(Modifier.testTag("member-usage"),verticalArrangement=Arrangement.spacedBy(12.dp)) { + if(!usage.available)Text(copyText(InsightsStrings.INSIGHTS_USAGE_UNAVAILABLE),color=MaterialTheme.colorScheme.onSurfaceVariant) + usage.loans?.let {loans->MemberPanel { + Text(copyText(InsightsStrings.INSIGHTS_LOANS),style=MaterialTheme.typography.titleLarge) + StatRow(listOf(copyText(InsightsStrings.INSIGHTS_BORROWED) to loans.count.toString(),copyText(InsightsStrings.INSIGHTS_RETURNED) to loans.returned.toString(), + copyText(InsightsStrings.INSIGHTS_OUT_NOW) to loans.currentlyOut.toString())) + SeriesBars(copyText(InsightsStrings.INSIGHTS_BY_MONTH),loans.monthly) + }} + usage.machines?.let {machines->MemberPanel { + Text(copyText(InsightsStrings.INSIGHTS_MACHINES),style=MaterialTheme.typography.titleLarge) + StatRow(listOf(copyText(InsightsStrings.INSIGHTS_MACHINE_HOURS) to machines.hours)) + SeriesBars(copyText(InsightsStrings.INSIGHTS_BY_MONTH),machines.monthly) + SeriesBars(copyText(InsightsStrings.INSIGHTS_TOP_MACHINES),machines.top) + }} + if(usage.bookings!=null || usage.eventsAttended!=null)MemberPanel { + StatRow(listOfNotNull(usage.bookings?.let {copyText(InsightsStrings.INSIGHTS_BOOKINGS) to it.count.toString()}, + usage.eventsAttended?.let {copyText(InsightsStrings.INSIGHTS_EVENTS_ATTENDED) to it.toString()})) + usage.bookings?.let {SeriesBars(copyText(InsightsStrings.INSIGHTS_BY_MONTH),it.monthly)} + } + usage.payments?.let {payments->MemberPanel { + Text(copyText(InsightsStrings.INSIGHTS_PAYMENTS),style=MaterialTheme.typography.titleLarge) + StatRow(listOfNotNull(payments.paidByCurrency.takeIf {it.isNotEmpty()}?.let {copyText(InsightsStrings.INSIGHTS_PAID) to Money(it)}, + payments.outstandingByCurrency.takeIf {it.isNotEmpty()}?.let {copyText(InsightsStrings.INSIGHTS_OUTSTANDING) to Money(it)})) + if(payments.paidByCurrency.isEmpty() && payments.outstandingByCurrency.isEmpty()) + Text(copyText(InsightsStrings.INSIGHTS_NO_ACTIVITY_YET),color=MaterialTheme.colorScheme.onSurfaceVariant) + }} + } + } + } +} + +private fun counterLabel(key:String)=when(key) { + "pending_requests"->InsightsStrings.INSIGHTS_PENDING_REQUESTS;"awaiting_issue"->InsightsStrings.INSIGHTS_AWAITING_ISSUE + "overdue_loans"->InsightsStrings.INSIGHTS_OVERDUE_LOANS;"pending_bookings"->InsightsStrings.INSIGHTS_PENDING_BOOKINGS + "upcoming_bookings"->InsightsStrings.INSIGHTS_UPCOMING_BOOKINGS;"upcoming_events"->InsightsStrings.INSIGHTS_UPCOMING_EVENTS + "pending_event_registrations"->InsightsStrings.INSIGHTS_PENDING_EVENT_REGISTRATIONS;"open_problem_reports"->InsightsStrings.INSIGHTS_OPEN_PROBLEM_REPORTS + "low_stock"->InsightsStrings.INSIGHTS_LOW_STOCK;"pending_prints"->InsightsStrings.INSIGHTS_PENDING_PRINTS;"active_prints"->InsightsStrings.INSIGHTS_ACTIVE_PRINTS + "prints_awaiting_collection"->InsightsStrings.INSIGHTS_PRINTS_AWAITING_COLLECTION;"maintenance_overdue"->InsightsStrings.INSIGHTS_MAINTENANCE_OVERDUE + "warranty_expiring"->InsightsStrings.INSIGHTS_WARRANTY_EXPIRING;"stocktakes_awaiting_approval"->InsightsStrings.INSIGHTS_STOCKTAKES_AWAITING_APPROVAL + "pending_payments"->InsightsStrings.INSIGHTS_PENDING_PAYMENTS;"failed_emails"->InsightsStrings.INSIGHTS_FAILED_EMAILS + "ready_to_issue"->InsightsStrings.INSIGHTS_READY_TO_ISSUE;"due_returns"->InsightsStrings.INSIGHTS_DUE_RETURNS + else->InsightsStrings.INSIGHTS_PICKUPS +} + +/** Staff landing card. Handover-only staff see their queue tiles and no analytics. */ +@Composable fun StaffDashboardPanel(holder:InsightsStateHolder) { + val copyText=rememberMobileText() + val s by holder.state.collectAsStateWithLifecycle() + val scope=rememberCoroutineScope() + LaunchedEffect(holder,s.scope) {if(s.scope!=null && s.dashboard.value==null && !s.dashboard.loading && s.dashboard.error==null)holder.refreshDashboard()} + MemberSection(copyText(InsightsStrings.INSIGHTS_TODAY_AT_A_GLANCE)) { + MemberLoaded(s.dashboard,{scope.launch {holder.refreshDashboard()}}) {dashboard-> + Column(Modifier.testTag("staff-dashboard"),verticalArrangement=Arrangement.spacedBy(12.dp)) { + if(dashboard.queue.isNotEmpty())MemberPanel { + Text(copyText(InsightsStrings.INSIGHTS_HANDOVER_QUEUE),style=MaterialTheme.typography.titleMedium) + StatRow(dashboard.queue.map {copyText(counterLabel(it.key)) to it.value.toString()}) + } + if(dashboard.counters.isNotEmpty())StatRow(dashboard.counters.map {copyText(counterLabel(it.key)) to it.value.toString()}) + if(dashboard.scope!=StaffScope.HANDOVER && dashboard.outstandingByCurrency.isNotEmpty()) + StatRow(listOf(copyText(InsightsStrings.INSIGHTS_OUTSTANDING_PAYMENTS) to Money(dashboard.outstandingByCurrency))) + if(dashboard.scope!=StaffScope.HANDOVER && dashboard.widgets.isNotEmpty())MemberPanel { + Text(copyText(InsightsStrings.INSIGHTS_REPORTS),style=MaterialTheme.typography.titleMedium) + dashboard.widgets.forEach {widget-> + Row(Modifier.fillMaxWidth().heightIn(min=48.dp).semantics(mergeDescendants=true) {},verticalAlignment=Alignment.CenterVertically) { + Text(widget.title,Modifier.weight(1f),style=MaterialTheme.typography.bodyLarge) + widget.headline?.let {Text(it,style=MaterialTheme.typography.titleMedium.copy(fontFeatureSettings="tnum"),color=MaterialTheme.colorScheme.onPrimaryContainer)} + } + } + } + if(dashboard.queue.isEmpty() && dashboard.counters.isEmpty() && (dashboard.scope==StaffScope.HANDOVER || dashboard.widgets.isEmpty())) + Text(copyText(InsightsStrings.INSIGHTS_NOTHING_NEEDS_ATTENTION),color=MaterialTheme.colorScheme.onSurfaceVariant) + } + } + BrandTextButton(copyText(InsightsStrings.INSIGHTS_REFRESH_DASHBOARD),{scope.launch {holder.refreshDashboard()}},!s.dashboard.loading) + } +} + +/** Per-makerspace SMS/WhatsApp consent. Each switch reflects only the server's returned state. */ +@Composable fun MessagingConsentScreen(holder:InsightsStateHolder,onBack:()->Unit,onAccount:(()->Unit)?) { + val copyText=rememberMobileText() + val s by holder.state.collectAsStateWithLifecycle() + val scope=rememberCoroutineScope() + androidx.activity.compose.BackHandler(s.consentBusy==null,onBack) + LaunchedEffect(holder,s.scope) {if(s.scope!=null)holder.refreshMessaging()} + Box(Modifier.fillMaxSize(),contentAlignment=Alignment.TopCenter) { + Column(Modifier.widthIn(max=720.dp).fillMaxWidth().verticalScroll(androidx.compose.foundation.rememberScrollState()).padding(24.dp),verticalArrangement=Arrangement.spacedBy(20.dp)) { + BrandTextButton(copyText(UiStrings.MOBILE_BACK),onBack,s.consentBusy==null) + Text(copyText(InsightsStrings.INSIGHTS_MESSAGES),style=MaterialTheme.typography.headlineLarge,modifier=Modifier.semantics {heading()}) + Text(copyText(InsightsStrings.INSIGHTS_MESSAGES_HELP),color=MaterialTheme.colorScheme.onSurfaceVariant) + BrandError(s.consentError) + MemberLoaded(s.messaging,{scope.launch {holder.refreshMessaging()}}) {consent-> + listOf("sms" to consent.sms,"whatsapp" to consent.whatsapp).forEach {(key,channel)-> + val title=copyText(if(key=="sms")InsightsStrings.INSIGHTS_SMS else InsightsStrings.INSIGHTS_WHATSAPP) + val busy=s.consentBusy==key + val enabled=s.consentBusy==null && (channel.consented || channel.canOptIn) + MemberPanel { + Row(Modifier.fillMaxWidth().heightIn(min=48.dp).toggleable(channel.consented,enabled,Role.Switch) {scope.launch {holder.setConsent(key,it)}} + .testTag("consent-$key"),verticalAlignment=Alignment.CenterVertically,horizontalArrangement=Arrangement.spacedBy(12.dp)) { + Column(Modifier.weight(1f),verticalArrangement=Arrangement.spacedBy(6.dp)) { + Text(title,style=MaterialTheme.typography.titleMedium) + StatusPill(copyText(if(channel.consented)InsightsStrings.INSIGHTS_ON else InsightsStrings.INSIGHTS_OFF),channel.consented) + } + if(busy)CircularProgressIndicator(Modifier.size(24.dp)) else Switch(channel.consented,null,enabled=enabled) + } + when { + !channel.available->Text(copyText(InsightsStrings.INSIGHTS_CHANNEL_UNAVAILABLE),color=MaterialTheme.colorScheme.onSurfaceVariant) + channel.suppressed->Text(copyText(InsightsStrings.INSIGHTS_SUPPRESSED)) + !channel.phoneVerified->{Text(copyText(InsightsStrings.INSIGHTS_VERIFY_PHONE_FIRST));onAccount?.let {BrandTextButton(copyText(InsightsStrings.INSIGHTS_OPEN_ACCOUNT),it)}} + channel.phoneMasked.isNotBlank()->Text(copyText(InsightsStrings.INSIGHTS_SENDING_TO,channel.phoneMasked),color=MaterialTheme.colorScheme.onSurfaceVariant) + } + } + } + } + } + } +} diff --git a/app/src/main/java/org/spaceworks/app/insights/InsightsStrings.kt b/app/src/main/java/org/spaceworks/app/insights/InsightsStrings.kt new file mode 100644 index 0000000..c6e679b --- /dev/null +++ b/app/src/main/java/org/spaceworks/app/insights/InsightsStrings.kt @@ -0,0 +1,65 @@ +// Generated from insights_strings.xml by generate_mobile_copy.py. Do not edit. +package org.spaceworks.app.insights + +import org.spaceworks.app.R +import org.spaceworks.app.platform.MobileCopyKey + +object InsightsStrings { + val INSIGHTS_YOUR_ACTIVITY=MobileCopyKey(R.string.insights_your_activity,"Your activity") + val INSIGHTS_USAGE_UNAVAILABLE=MobileCopyKey(R.string.insights_usage_unavailable,"Activity details aren't available for this makerspace.") + val INSIGHTS_LOANS=MobileCopyKey(R.string.insights_loans,"Loans") + val INSIGHTS_BORROWED=MobileCopyKey(R.string.insights_borrowed,"Borrowed") + val INSIGHTS_RETURNED=MobileCopyKey(R.string.insights_returned,"Returned") + val INSIGHTS_OUT_NOW=MobileCopyKey(R.string.insights_out_now,"Out now") + val INSIGHTS_MACHINES=MobileCopyKey(R.string.insights_machines,"Machine time") + val INSIGHTS_MACHINE_HOURS=MobileCopyKey(R.string.insights_machine_hours,"Hours") + val INSIGHTS_TOP_MACHINES=MobileCopyKey(R.string.insights_top_machines,"Most used machines") + val INSIGHTS_BOOKINGS=MobileCopyKey(R.string.insights_bookings,"Bookings") + val INSIGHTS_EVENTS_ATTENDED=MobileCopyKey(R.string.insights_events_attended,"Events attended") + val INSIGHTS_PAYMENTS=MobileCopyKey(R.string.insights_payments,"Payments") + val INSIGHTS_PAID=MobileCopyKey(R.string.insights_paid,"Paid") + val INSIGHTS_OUTSTANDING=MobileCopyKey(R.string.insights_outstanding,"Outstanding") + val INSIGHTS_BY_MONTH=MobileCopyKey(R.string.insights_by_month,"By month") + val INSIGHTS_NO_ACTIVITY_YET=MobileCopyKey(R.string.insights_no_activity_yet,"No activity in this period yet.") + val INSIGHTS_POINT_VALUE=MobileCopyKey(R.string.insights_point_value,"%1\$s: %2\$s %3\$s") + val INSIGHTS_MONEY_VALUE=MobileCopyKey(R.string.insights_money_value,"%1\$s %2\$s") + val INSIGHTS_REFRESH_ACTIVITY=MobileCopyKey(R.string.insights_refresh_activity,"Refresh activity") + val INSIGHTS_LOADING=MobileCopyKey(R.string.insights_loading,"Loading activity") + val INSIGHTS_TODAY_AT_A_GLANCE=MobileCopyKey(R.string.insights_today_at_a_glance,"Today at a glance") + val INSIGHTS_HANDOVER_QUEUE=MobileCopyKey(R.string.insights_handover_queue,"Handover queue") + val INSIGHTS_READY_TO_ISSUE=MobileCopyKey(R.string.insights_ready_to_issue,"Ready to issue") + val INSIGHTS_DUE_RETURNS=MobileCopyKey(R.string.insights_due_returns,"Due back") + val INSIGHTS_PICKUPS=MobileCopyKey(R.string.insights_pickups,"Pickups") + val INSIGHTS_NOTHING_NEEDS_ATTENTION=MobileCopyKey(R.string.insights_nothing_needs_attention,"Nothing needs your attention right now.") + val INSIGHTS_OUTSTANDING_PAYMENTS=MobileCopyKey(R.string.insights_outstanding_payments,"Outstanding payments") + val INSIGHTS_REPORTS=MobileCopyKey(R.string.insights_reports,"Reports") + val INSIGHTS_REFRESH_DASHBOARD=MobileCopyKey(R.string.insights_refresh_dashboard,"Refresh dashboard") + val INSIGHTS_PENDING_REQUESTS=MobileCopyKey(R.string.insights_pending_requests,"Pending requests") + val INSIGHTS_AWAITING_ISSUE=MobileCopyKey(R.string.insights_awaiting_issue,"Awaiting issue") + val INSIGHTS_OVERDUE_LOANS=MobileCopyKey(R.string.insights_overdue_loans,"Overdue loans") + val INSIGHTS_PENDING_BOOKINGS=MobileCopyKey(R.string.insights_pending_bookings,"Pending bookings") + val INSIGHTS_UPCOMING_BOOKINGS=MobileCopyKey(R.string.insights_upcoming_bookings,"Upcoming bookings") + val INSIGHTS_UPCOMING_EVENTS=MobileCopyKey(R.string.insights_upcoming_events,"Upcoming events") + val INSIGHTS_PENDING_EVENT_REGISTRATIONS=MobileCopyKey(R.string.insights_pending_event_registrations,"Pending event registrations") + val INSIGHTS_OPEN_PROBLEM_REPORTS=MobileCopyKey(R.string.insights_open_problem_reports,"Open problem reports") + val INSIGHTS_LOW_STOCK=MobileCopyKey(R.string.insights_low_stock,"Low stock") + val INSIGHTS_PENDING_PRINTS=MobileCopyKey(R.string.insights_pending_prints,"Pending prints") + val INSIGHTS_ACTIVE_PRINTS=MobileCopyKey(R.string.insights_active_prints,"Active prints") + val INSIGHTS_PRINTS_AWAITING_COLLECTION=MobileCopyKey(R.string.insights_prints_awaiting_collection,"Prints awaiting collection") + val INSIGHTS_MAINTENANCE_OVERDUE=MobileCopyKey(R.string.insights_maintenance_overdue,"Maintenance overdue") + val INSIGHTS_WARRANTY_EXPIRING=MobileCopyKey(R.string.insights_warranty_expiring,"Warranty expiring") + val INSIGHTS_STOCKTAKES_AWAITING_APPROVAL=MobileCopyKey(R.string.insights_stocktakes_awaiting_approval,"Stocktakes awaiting approval") + val INSIGHTS_PENDING_PAYMENTS=MobileCopyKey(R.string.insights_pending_payments,"Pending payments") + val INSIGHTS_FAILED_EMAILS=MobileCopyKey(R.string.insights_failed_emails,"Failed emails") + val INSIGHTS_MESSAGES=MobileCopyKey(R.string.insights_messages,"Text and WhatsApp messages") + val INSIGHTS_MESSAGES_HELP=MobileCopyKey(R.string.insights_messages_help,"Choose whether this makerspace can reach you by text message or WhatsApp. App notifications and email are set separately.") + val INSIGHTS_SMS=MobileCopyKey(R.string.insights_sms,"Text messages (SMS)") + val INSIGHTS_WHATSAPP=MobileCopyKey(R.string.insights_whatsapp,"WhatsApp") + val INSIGHTS_ON=MobileCopyKey(R.string.insights_on,"On") + val INSIGHTS_OFF=MobileCopyKey(R.string.insights_off,"Off") + val INSIGHTS_SENDING_TO=MobileCopyKey(R.string.insights_sending_to,"Messages go to %1\$s.") + val INSIGHTS_VERIFY_PHONE_FIRST=MobileCopyKey(R.string.insights_verify_phone_first,"Verify your phone number in Account and security before turning this on.") + val INSIGHTS_CHANNEL_UNAVAILABLE=MobileCopyKey(R.string.insights_channel_unavailable,"This makerspace doesn't send these messages.") + val INSIGHTS_SUPPRESSED=MobileCopyKey(R.string.insights_suppressed,"Messages to this number are paused after a STOP reply. Contact your makerspace to resume them.") + val INSIGHTS_OPEN_ACCOUNT=MobileCopyKey(R.string.insights_open_account,"Open Account and security") +} diff --git a/app/src/main/java/org/spaceworks/app/notifications/LinkStateHolder.kt b/app/src/main/java/org/spaceworks/app/notifications/LinkStateHolder.kt index b8b6100..323dd44 100644 --- a/app/src/main/java/org/spaceworks/app/notifications/LinkStateHolder.kt +++ b/app/src/main/java/org/spaceworks/app/notifications/LinkStateHolder.kt @@ -47,7 +47,7 @@ class LinkStateHolder(private val server:HttpUrl,private val authorize:suspend(I mutable.update {it.copy(busy=true,error=null)} try { val destination=if(target!=null){if(authorize(target.makerspaceId))target else null} - else requireNotNull(legacy).let {alias->authorizeSlug(alias.slug,alias.staff)?.takeIf {id->id>0&&(requiredMaker==null||requiredMaker==id)}?.let {id->MobileDestination(id,if(alias.staff)MobileSection.INBOX else MobileSection.HOME)}} + else requireNotNull(legacy).let {alias->authorizeSlug(alias.slug,alias.staff)?.takeIf {id->id>0&&(requiredMaker==null||requiredMaker==id)}?.let {id->MobileDestination(id,alias.section,alias.resource)}} if(request!=sequence.get())continue if(destination==null){mutable.update {it.copy(busy=false,pending=null,pendingLegacy=null,requiredMaker=null,error=text(CoreStrings.CORE_YOUR_MAKERSPACE_ACCESS_COULDN_T_BE_CONFIRMED_OPEN_INBOX_OR_T))};return} select(destination.makerspaceId) diff --git a/app/src/main/java/org/spaceworks/app/notifications/MobileRoutes.kt b/app/src/main/java/org/spaceworks/app/notifications/MobileRoutes.kt index 21e2b1f..2e4006f 100644 --- a/app/src/main/java/org/spaceworks/app/notifications/MobileRoutes.kt +++ b/app/src/main/java/org/spaceworks/app/notifications/MobileRoutes.kt @@ -7,27 +7,47 @@ import java.util.UUID enum class MobileSection { HOME, INVENTORY, ACTIVITY, INBOX, PROFILE, PAYMENTS, BOOKINGS, EVENTS, REGISTRATION, SPACES } data class MobileDestination(val makerspaceId:Int,val section:MobileSection,val resourceId:String?=null) -data class LegacyMemberRoute(val slug:String,val staff:Boolean) +/** A slug-addressed web landing from the server link contract; the slug is always freshly authorized. */ +data class LegacyMemberRoute(val slug:String,val staff:Boolean, + val section:MobileSection=if(staff)MobileSection.INBOX else MobileSection.HOME,val resource:String?=null) /** Product routes, not browser/API paths. Neither a link nor a push can select a deployment. */ object MobileRoutes { fun hint(value:PushHint):MobileDestination = if(value.version==2) value.link?.let(::relative)?.takeIf {it.makerspaceId==value.makerspaceId} + // A verified custom-domain form names no slug: the hint's own makerspace owns it. + ?: value.link?.let {link->PushHint.safeInAppPath(link)?.let {contract(segments(it))}}?.let {(section,id)->MobileDestination(value.makerspaceId,section,id)} ?: MobileDestination(value.makerspaceId,MobileSection.INBOX) else MobileDestination(value.makerspaceId,MobileSection.INBOX) + private fun segments(path:String)=path.removePrefix("/").removeSuffix("/").split('/') + private val tabs=mapOf("overview" to MobileSection.HOME,"loans" to MobileSection.ACTIVITY,"bookings" to MobileSection.BOOKINGS, + "events" to MobileSection.EVENTS,"payments" to MobileSection.PAYMENTS,"inbox" to MobileSection.INBOX, + "account" to MobileSection.PROFILE,"membership" to MobileSection.PROFILE) + private fun uuid(value:String)=runCatching {UUID.fromString(value).toString().takeIf {it==value.lowercase()}}.getOrNull() + /** + * Member destinations of the server's mobile link contract (backend mobile_link_contract.py), + * without the `/m/{slug}` prefix. Staff/admin/control/API paths are never claimed. + */ + private fun contract(parts:List):Pair? = when { + parts==listOf("member") -> MobileSection.HOME to null + parts.size==2 && parts[0]=="member" -> (tabs[parts[1]]?:MobileSection.HOME) to null + parts.size==4 && parts[0]=="member" && parts[1]=="events" && parts[3]=="ticket" -> positive(parts[2])?.let {MobileSection.REGISTRATION to it.toString()} + parts.size==2 && parts[0]=="events" -> uuid(parts[1])?.let {MobileSection.EVENTS to it} + parts.size==2 && parts[0]=="bookings" -> uuid(parts[1])?.let {MobileSection.BOOKINGS to it} + parts.size==2 && parts[0]=="items" -> positive(parts[1])?.let {MobileSection.INVENTORY to it.toString()} + else -> null + } fun external(raw:String,server:HttpUrl,scheme:String="spaceworks"):MobileDestination? = externalPath(raw,server,scheme)?.let(::relative) /** Exact published web landings only; caller freshly resolves slug and native staff authority. */ fun legacy(raw:String,server:HttpUrl,scheme:String="spaceworks"):LegacyMemberRoute? = externalPath(raw,server,scheme)?.let(::legacyPath) fun legacyPath(raw:String):LegacyMemberRoute? { - val parts=PushHint.safeInAppPath(raw)?.removePrefix("/")?.split('/')?:return null + val parts=PushHint.safeInAppPath(raw)?.let(::segments)?:return null val slug=parts.getOrNull(1)?.takeIf {it.matches(Regex("[A-Za-z0-9_-]{1,256}"))}?:return null if(parts[0]!="m")return null - return when { - parts.size==3 && parts[2]=="member"->LegacyMemberRoute(slug,false) - parts.size==4 && parts[2]=="admin" && parts[3]=="notifications"->LegacyMemberRoute(slug,true) - else->null - } + if(parts.size==4 && parts[2]=="admin" && parts[3]=="notifications")return LegacyMemberRoute(slug,true) + val (section,resource)=contract(parts.drop(2))?:return null + return LegacyMemberRoute(slug,false,section,resource) } private fun externalPath(raw:String,server:HttpUrl,scheme:String):String? { if(raw.toByteArray(Charsets.UTF_8).size>2048 || raw.any {it=='%'||it=='\\'||it.isISOControl()||Character.getType(it)==Character.FORMAT.toInt()})return null diff --git a/app/src/main/java/org/spaceworks/app/payments/GeneratedPaymentsGateway.kt b/app/src/main/java/org/spaceworks/app/payments/GeneratedPaymentsGateway.kt index 1404b7c..6c5e837 100644 --- a/app/src/main/java/org/spaceworks/app/payments/GeneratedPaymentsGateway.kt +++ b/app/src/main/java/org/spaceworks/app/payments/GeneratedPaymentsGateway.kt @@ -14,6 +14,7 @@ import org.spaceworks.app.auth.requireBody import org.spaceworks.app.member.MemberScope import org.spaceworks.app.network.ApiSession import org.spaceworks.generated.api.PaymentsApi +import org.spaceworks.generated.model.MobilePaymentIntentResponse /** The signed API URL is parsed into generated arguments; it is never opened directly. */ object PaymentDocumentLinks { @@ -59,7 +60,14 @@ class GeneratedPaymentsGateway( } override suspend fun intent(scope:MemberScope,paymentId:String):PaymentIntent { val value=api(scope).apiV1MemberMakerspacesPaymentsMobileIntentCreate(scope.id,positivePaymentId(paymentId)).requireBody() - return PaymentIntent(value.paymentId.toString(),value.clientSecret,value.publishableKey,value.customerId,value.ephemeralKey) + return when(value) { + is MobilePaymentIntentResponse.StripeWrapper -> value.value.let { + PaymentIntent.Stripe(it.paymentId.toString(),it.clientSecret,it.publishableKey,it.customerId,it.ephemeralKey) + } + is MobilePaymentIntentResponse.RazorpayWrapper -> value.value.let { + PaymentIntent.Razorpay(it.paymentId.toString(),it.keyId,it.orderId,it.amountMinor.toLong(),it.currency,it.name,it.description) + } + } } override suspend fun documents(scope:MemberScope,page:Int):PaymentDocumentPage { require(page>0) diff --git a/app/src/main/java/org/spaceworks/app/payments/PaymentsGateway.kt b/app/src/main/java/org/spaceworks/app/payments/PaymentsGateway.kt index 216fb3e..21a3c29 100644 --- a/app/src/main/java/org/spaceworks/app/payments/PaymentsGateway.kt +++ b/app/src/main/java/org/spaceworks/app/payments/PaymentsGateway.kt @@ -10,10 +10,21 @@ data class PaymentRefund(val amount:String,val currency:String,val status:String data class PaymentDocument(val id:String,val paymentId:String,val number:String,val kind:String,val amount:String,val currency:String,val issuedAt:String) data class PaymentDocumentPage(val rows:List,val nextPage:Int?,val count:Int) -/** Ephemeral SDK input, never placed in persisted state, logs or a StateFlow. */ -class PaymentIntent(val paymentId:String,val clientSecret:String,val publishableKey:String,val customerId:String?,val ephemeralKey:String?) { +/** Ephemeral SDK input, never placed in persisted state, logs or a StateFlow. The server selects the vendor. */ +sealed class PaymentIntent(val paymentId:String) { + class Stripe(paymentId:String,val clientSecret:String,val publishableKey:String,val customerId:String?,val ephemeralKey:String?):PaymentIntent(paymentId) { + override fun valid()=publishableKey.startsWith("pk_") && clientSecret.isNotBlank() && (customerId.isNullOrBlank()==ephemeralKey.isNullOrBlank()) + } + /** [amountMinor] is the server's integer minor-unit amount; it is passed to the SDK unchanged. */ + class Razorpay(paymentId:String,val keyId:String,val orderId:String,val amountMinor:Long,val currency:String,val name:String,val description:String):PaymentIntent(paymentId) { + override fun valid()=keyId.startsWith("rzp_") && orderId.startsWith("order_") && amountMinor>0 && Regex("[A-Za-z]{3}").matches(currency) + } + abstract fun valid():Boolean override fun toString()="PaymentIntent(redacted)" } +/** Stripe intent shorthand used by fixtures and the Stripe rail. */ +fun PaymentIntent(paymentId:String,clientSecret:String,publishableKey:String,customerId:String?,ephemeralKey:String?):PaymentIntent= + PaymentIntent.Stripe(paymentId,clientSecret,publishableKey,customerId,ephemeralKey) class PreparedPayment(val reference:String,val intent:PaymentIntent) { override fun toString()="PreparedPayment(redacted)" } diff --git a/app/src/main/java/org/spaceworks/app/payments/PaymentsScreen.kt b/app/src/main/java/org/spaceworks/app/payments/PaymentsScreen.kt index aa66754..1f837e7 100644 --- a/app/src/main/java/org/spaceworks/app/payments/PaymentsScreen.kt +++ b/app/src/main/java/org/spaceworks/app/payments/PaymentsScreen.kt @@ -26,7 +26,7 @@ import org.spaceworks.app.ui.* /** The Activity registers adapters before STARTED and passes their current callback state here. */ @Composable fun PaymentsScreen(holder:PaymentsStateHolder,demo:Boolean,onBack:()->Unit, nativeState:NativePaymentState=NativePaymentState.Idle, - launchStripe:(StripeSheetInput)->PaymentLaunchResult={PaymentLaunchResult.HOST_NOT_READY}, + launchCheckout:(NativeSheetInput)->PaymentLaunchResult={PaymentLaunchResult.HOST_NOT_READY}, acknowledgeNative:()->Unit={},recoverInterrupted:()->Unit={},refreshOnEnter:Boolean=true,hostAvailable:Boolean=true) { val text=rememberMobileText() val s by holder.state.collectAsStateWithLifecycle();val work=rememberCoroutineScope();val context=LocalContext.current @@ -112,8 +112,11 @@ import org.spaceworks.app.ui.* BrandButton(text(PaymentStrings.PAYMENT_CONTINUE_TO_PAYMENT),{ if(demo)demoCheckout=row.id else work.launch{ val prepared=holder.prepareCheckout(row.id)?:return@launch - val input=prepared.intent - val result=launchStripe(StripeSheetInput(prepared.reference,context.applicationInfo.loadLabel(context.packageManager).toString(),input.publishableKey,input.clientSecret,input.customerId,input.ephemeralKey)) + val appName=context.applicationInfo.loadLabel(context.packageManager).toString() + val result=launchCheckout(when(val input=prepared.intent){ + is PaymentIntent.Stripe->StripeSheetInput(prepared.reference,appName,input.publishableKey,input.clientSecret,input.customerId,input.ephemeralKey) + is PaymentIntent.Razorpay->RazorpaySheetInput(prepared.reference,input.name.ifBlank {appName},input.keyId,input.orderId,input.amountMinor,input.currency,input.description) + }) if(result!=PaymentLaunchResult.STARTED)holder.sdkClosed(prepared.reference,SheetCloseReason.FAILED,when(result){PaymentLaunchResult.HOST_NOT_READY->text(PaymentStrings.PAYMENT_CHECKOUT_IS_NOT_READY_TRY_AGAIN_FROM_PAYMENT_DETAILS);PaymentLaunchResult.ALREADY_PRESENTING,PaymentLaunchResult.AWAITING_SERVER_REFRESH->text(PaymentStrings.PAYMENT_FINISH_THE_CURRENT_CHECKOUT_AND_CHECK_ITS_PAYMENT_STATUS_BEF);else->text(PaymentStrings.PAYMENT_PAYMENT_SETUP_IS_UNAVAILABLE_CONTACT_YOUR_MAKERSPACE)}) } },enabled&&!providerBusy) diff --git a/app/src/main/java/org/spaceworks/app/payments/PaymentsStateHolder.kt b/app/src/main/java/org/spaceworks/app/payments/PaymentsStateHolder.kt index c3d80f6..ff175b2 100644 --- a/app/src/main/java/org/spaceworks/app/payments/PaymentsStateHolder.kt +++ b/app/src/main/java/org/spaceworks/app/payments/PaymentsStateHolder.kt @@ -87,7 +87,7 @@ class PaymentsStateHolder(private val api:PaymentsGateway,private val files:Paym val current=record(id) if(current?.status!="pending" || !current.onlineAvailable){stop(id,text(PaymentStrings.PAYMENT_THIS_PAYMENT_IS_NO_LONGER_AVAILABLE_TO_PAY_ONLINE_ITS_LATEST));return null} val input=api.intent(scope,id);if(!matches(expected,reference))return null - require(input.paymentId==id && input.publishableKey.startsWith("pk_") && input.clientSecret.isNotBlank() && (input.customerId.isNullOrBlank()==input.ephemeralKey.isNullOrBlank())) {text(PaymentStrings.PAYMENT_PAYMENT_SETUP_IS_UNAVAILABLE_CONTACT_YOUR_MAKERSPACE)} + require(input.paymentId==id && input.valid()) {text(PaymentStrings.PAYMENT_PAYMENT_SETUP_IS_UNAVAILABLE_CONTACT_YOUR_MAKERSPACE)} mutable.update {it.copy(checkout=PaymentCheckout(PaymentCheckoutPhase.PRESENTING,id,reference))} return PreparedPayment(reference,input) }catch(c:CancellationException){if(matches(expected,reference))stop(id,text(PaymentStrings.PAYMENT_CHECKOUT_WAS_INTERRUPTED_CHECK_THE_PAYMENT_STATUS_BEFORE_TRY));throw c} diff --git a/app/src/main/java/org/spaceworks/app/platform/NativeCheckoutBindings.kt b/app/src/main/java/org/spaceworks/app/platform/NativeCheckoutBindings.kt index 077c651..ae0e765 100644 --- a/app/src/main/java/org/spaceworks/app/platform/NativeCheckoutBindings.kt +++ b/app/src/main/java/org/spaceworks/app/platform/NativeCheckoutBindings.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.StateFlow /** Activity-owned bindings. Never retain this object in a controller or account cache. */ class NativeCheckoutBindings( val state:StateFlow, - val launchStripe:(StripeSheetInput)->PaymentLaunchResult, + val launchCheckout:(NativeSheetInput)->PaymentLaunchResult, val acknowledge:()->Unit, val recoverInterrupted:()->Unit, ) diff --git a/app/src/main/java/org/spaceworks/app/platform/NativePayments.kt b/app/src/main/java/org/spaceworks/app/platform/NativePayments.kt index 030935d..e529bfc 100644 --- a/app/src/main/java/org/spaceworks/app/platform/NativePayments.kt +++ b/app/src/main/java/org/spaceworks/app/platform/NativePayments.kt @@ -109,15 +109,17 @@ class NativePaymentViewModel(handle: SavedStateHandle) : ViewModel() { } /** Values copied from a generated gateway response; no amount calculation or API request lives here. */ -class StripeSheetInput(val paymentReference: String, val merchantName: String, val publishableKey: String, - val clientSecret: String, val customerId: String? = null, val ephemeralKey: String? = null) { +sealed interface NativeSheetInput { val paymentReference: String } + +class StripeSheetInput(override val paymentReference: String, val merchantName: String, val publishableKey: String, + val clientSecret: String, val customerId: String? = null, val ephemeralKey: String? = null) : NativeSheetInput { fun valid(): Boolean = paymentReference.isNotBlank() && merchantName.isNotBlank() && publishableKey.startsWith("pk_") && clientSecret.isNotBlank() && (customerId.isNullOrBlank() == ephemeralKey.isNullOrBlank()) override fun toString() = "StripeSheetInput(redacted)" } -class RazorpaySheetInput(val paymentReference: String, val merchantName: String, val keyId: String, - val orderId: String, val amountMinor: Long, val currency: String) { +class RazorpaySheetInput(override val paymentReference: String, val merchantName: String, val keyId: String, + val orderId: String, val amountMinor: Long, val currency: String, val description: String = "") : NativeSheetInput { fun valid(): Boolean = paymentReference.isNotBlank() && merchantName.isNotBlank() && keyId.startsWith("rzp_") && orderId.startsWith("order_") && amountMinor > 0 && Regex("[A-Za-z]{3}").matches(currency) override fun toString() = "RazorpaySheetInput(redacted)" @@ -190,6 +192,7 @@ private class RazorpayCheckoutContract : ActivityResultContract SheetCloseReason.SUBMITTED intent?.getBooleanExtra("interrupted", false) == true -> SheetCloseReason.INTERRUPTED @@ -221,11 +224,13 @@ class RazorpayCheckoutActivity : ComponentActivity(), PaymentResultWithDataListe } val input = RazorpaySheetInput(intent.getStringExtra("reference").orEmpty(), intent.getStringExtra("merchant").orEmpty(), intent.getStringExtra("key").orEmpty(), intent.getStringExtra("order").orEmpty(), - intent.getLongExtra("amount", 0), intent.getStringExtra("currency").orEmpty()) + intent.getLongExtra("amount", 0), intent.getStringExtra("currency").orEmpty(), + intent.getStringExtra("description").orEmpty()) if (!input.valid()) { fail(); return } try { val options = JSONObject().put("name", input.merchantName).put("order_id", input.orderId) .put("amount", input.amountMinor).put("currency", input.currency) + .apply { if (input.description.isNotBlank()) put("description", input.description) } .put("theme.color",razorpayBrandColor()) Checkout().apply { setKeyID(input.keyId) }.open(this, options) } catch (_: Exception) { fail() } diff --git a/app/src/main/java/org/spaceworks/app/platform/OidcBrowser.kt b/app/src/main/java/org/spaceworks/app/platform/OidcBrowser.kt new file mode 100644 index 0000000..68f3813 --- /dev/null +++ b/app/src/main/java/org/spaceworks/app/platform/OidcBrowser.kt @@ -0,0 +1,93 @@ +package org.spaceworks.app.platform + +import android.app.Activity +import android.content.Intent +import android.net.Uri +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeout +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.FormBody +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.OkHttpClient +import okhttp3.Request +import org.spaceworks.app.auth.AuthCancelled +import java.security.MessageDigest +import java.security.SecureRandom +import java.util.Base64 +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference + +/** + * One pending OIDC browser redirect. The app's own scheme carries only the provider's authorization + * response back; it never selects a server or becomes a navigation link. + */ +object OidcRedirects { + private val pending=AtomicReference?>() + fun redirectUri(applicationId:String)="$applicationId:/oidc" + fun begin():CompletableDeferred = CompletableDeferred().also {pending.getAndSet(it)?.cancel()} + val waiting get()=pending.get()?.isActive==true + /** Returns true when [intent] was an OIDC redirect; it is consumed whether or not a sign-in awaits it. */ + fun consume(intent:Intent,applicationId:String):Boolean { + val data=intent.data?:return false + if(data.scheme!=applicationId || data.path!="/oidc")return false + intent.data=null + pending.getAndSet(null)?.complete(data) + return true + } + /** The person returned to the app without a redirect: the browser step was abandoned. */ + fun abandon() {pending.getAndSet(null)?.cancel()} +} + +/** Authorization-code + PKCE (S256) against the published issuer; returns the provider ID token. */ +class OidcBrowser(private val client:OkHttpClient=OkHttpClient.Builder().followRedirects(false).followSslRedirects(false) + .callTimeout(30,TimeUnit.SECONDS).build()) { + private val json=Json {ignoreUnknownKeys=true} + private val random=SecureRandom() + private fun token(bytes:Int)=ByteArray(bytes).also(random::nextBytes).let {Base64.getUrlEncoder().withoutPadding().encodeToString(it)} + private fun https(value:String?):HttpUrl? = value?.toHttpUrlOrNull()?.takeIf {it.isHttps && it.username.isEmpty() && it.password.isEmpty()} + + suspend fun idToken(activity:Activity,issuer:String,clientId:String,nonce:String,redirectUri:String):String { + val base=https(issuer.trimEnd('/'))?:throw PlatformUnavailable("Invalid issuer") + val discovery=withContext(Dispatchers.IO) {get(base.newBuilder().addPathSegments(".well-known/openid-configuration").build())} + val authorize=https(discovery["authorization_endpoint"]?.jsonPrimitive?.contentOrNull)?:throw PlatformUnavailable("No authorization endpoint") + val tokenEndpoint=https(discovery["token_endpoint"]?.jsonPrimitive?.contentOrNull)?:throw PlatformUnavailable("No token endpoint") + val verifier=token(48) + val challenge=Base64.getUrlEncoder().withoutPadding().encodeToString(MessageDigest.getInstance("SHA-256").digest(verifier.toByteArray(Charsets.US_ASCII))) + val state=token(24) + val url=authorize.newBuilder().addQueryParameter("response_type","code").addQueryParameter("client_id",clientId) + .addQueryParameter("redirect_uri",redirectUri).addQueryParameter("scope","openid email profile") + .addQueryParameter("state",state).addQueryParameter("nonce",nonce) + .addQueryParameter("code_challenge",challenge).addQueryParameter("code_challenge_method","S256").build() + val redirect=OidcRedirects.begin() + withContext(Dispatchers.Main) { + activity.startActivity(Intent(Intent.ACTION_VIEW,Uri.parse(url.toString())).addCategory(Intent.CATEGORY_BROWSABLE).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) + } + val response=try {withTimeout(10*60_000L) {redirect.await()}} + catch(timeout:kotlinx.coroutines.TimeoutCancellationException) {OidcRedirects.abandon();throw AuthCancelled()} + catch(cancelled:kotlinx.coroutines.CancellationException) { + // An abandoned browser step is a user cancellation; caller cancellation still propagates. + if(redirect.isCancelled && kotlinx.coroutines.currentCoroutineContext()[kotlinx.coroutines.Job]?.isActive!=false)throw AuthCancelled() + throw cancelled + } + if(response.getQueryParameter("state")!=state)throw PlatformUnavailable("State mismatch") + response.getQueryParameter("error")?.let {if(it=="access_denied")throw AuthCancelled() else throw PlatformUnavailable("Provider refused")} + val code=response.getQueryParameter("code")?.takeIf {it.isNotBlank()}?:throw PlatformUnavailable("No code") + val tokens=withContext(Dispatchers.IO) { + post(tokenEndpoint,FormBody.Builder().add("grant_type","authorization_code").add("code",code) + .add("redirect_uri",redirectUri).add("client_id",clientId).add("code_verifier",verifier).build()) + } + return tokens["id_token"]?.jsonPrimitive?.contentOrNull?.takeIf {it.isNotBlank()}?:throw PlatformUnavailable("No ID token") + } + private fun get(url:HttpUrl)=execute(Request.Builder().url(url).header("Accept","application/json").build()) + private fun post(url:HttpUrl,body:FormBody)=execute(Request.Builder().url(url).post(body).header("Accept","application/json").build()) + private fun execute(request:Request):JsonObject = client.newCall(request).execute().use {response-> + if(!response.isSuccessful)throw PlatformUnavailable("Provider HTTP ${response.code}") + json.parseToJsonElement(response.body?.string().orEmpty()) as? JsonObject ?: throw PlatformUnavailable("Provider response") + } +} diff --git a/app/src/main/java/org/spaceworks/app/staff/GeneratedStaffGateway.kt b/app/src/main/java/org/spaceworks/app/staff/GeneratedStaffGateway.kt index 9b23b85..e16b68d 100644 --- a/app/src/main/java/org/spaceworks/app/staff/GeneratedStaffGateway.kt +++ b/app/src/main/java/org/spaceworks/app/staff/GeneratedStaffGateway.kt @@ -95,7 +95,7 @@ class GeneratedStaffGateway(private val session:ApiSession,private val authorize events(scope).apiV1AdminEventsCheckInResolveCreate(eventId,body).requireBody() } override suspend fun markAttended(scope:MemberScope,registrationId:Int,body:EventAttendanceMark)=checked(scope,"manage_events"){ - requireId(registrationId);events(scope).apiV1AdminEventRegistrationsMarkAttendedCreate(registrationId,body).requireBody().also{require(it.id==registrationId)} + requireId(registrationId);events(scope).apiV1AdminEventRegistrationsMarkAttendedCreate(registrationId,body).requireBody().also{require(it.registrationId==registrationId)} } override suspend fun spaces(scope:MemberScope,page:Int)=checked(scope){ requireId(page);val result=bookings(scope).apiV1AdminMakerspacesSpacesRetrieve(scope.id,page=page).requireBody() diff --git a/app/src/main/java/org/spaceworks/app/staff/StaffGateway.kt b/app/src/main/java/org/spaceworks/app/staff/StaffGateway.kt index 125ff2b..75f372b 100644 --- a/app/src/main/java/org/spaceworks/app/staff/StaffGateway.kt +++ b/app/src/main/java/org/spaceworks/app/staff/StaffGateway.kt @@ -28,7 +28,7 @@ interface StaffGateway { suspend fun kitPreview(scope:MemberScope,body:KitPreviewInput):KitPreview suspend fun events(scope:MemberScope,page:Int):EventListWithDefaults suspend fun resolveCheckIn(scope:MemberScope,eventId:Int,body:EventCheckInResolveRequest):EventCheckInResolveResponse - suspend fun markAttended(scope:MemberScope,registrationId:Int,body:EventAttendanceMark):EventRegistrationAdmin + suspend fun markAttended(scope:MemberScope,registrationId:Int,body:EventAttendanceMark):EventAttendanceResult suspend fun spaces(scope:MemberScope,page:Int):BookableSpaceListResponse suspend fun bookings(scope:MemberScope,spaceId:Int,page:Int):BookingListResponse suspend fun approveBooking(scope:MemberScope,id:Int):BookingAdmin @@ -40,3 +40,13 @@ class StaffEvidenceUpload(val evidenceId:Int,val uploadUrl:String,val method:Str val fields:Map,val headers:Map) { override fun toString()="StaffEvidenceUpload(redacted)" } + +/** The attendance response is a generated union; both arms carry the registration ID and its status. */ +val EventAttendanceResult.registrationId:Int get()=when(this){ + is EventAttendanceResult.EventRegistrationAdminValue->value.id + is EventAttendanceResult.EventCheckInResultValue->value.id +} +val EventAttendanceResult.attendanceStatus:String get()=when(this){ + is EventAttendanceResult.EventRegistrationAdminValue->value.status.value + is EventAttendanceResult.EventCheckInResultValue->value.status +} diff --git a/app/src/main/java/org/spaceworks/app/staff/StaffScreen.kt b/app/src/main/java/org/spaceworks/app/staff/StaffScreen.kt index 4b128fa..de04fbe 100644 --- a/app/src/main/java/org/spaceworks/app/staff/StaffScreen.kt +++ b/app/src/main/java/org/spaceworks/app/staff/StaffScreen.kt @@ -61,7 +61,7 @@ private data class StaffConfirmation(val title:String,val detail:String,val acti /** All physical mutations require visible review and confirmation. Camera callbacks resolve only. */ @OptIn(ExperimentalLayoutApi::class) -@Composable fun StaffScreen(holder:StaffStateHolder,demo:Boolean,onBack:()->Unit,previewScope:org.spaceworks.app.member.MemberScope?=null){ +@Composable fun StaffScreen(holder:StaffStateHolder,demo:Boolean,onBack:()->Unit,previewScope:org.spaceworks.app.member.MemberScope?=null,dashboard:(@Composable ()->Unit)?=null){ val text=rememberMobileText() val s by holder.state.collectAsStateWithLifecycle();val work=rememberCoroutineScope() @@ -130,6 +130,7 @@ private data class StaffConfirmation(val title:String,val detail:String,val acti if(s.busy||s.loading.isNotEmpty())LinearProgressIndicator(Modifier.fillMaxWidth().semantics{contentDescription=text(StaffStrings.STAFF_LOADING_STAFF_WORKSPACE)}) BrandError(s.error);s.notice?.let{Text(it,Modifier.semantics{liveRegion=LiveRegionMode.Polite})} if(clock val sectionSelected=s.section==section diff --git a/app/src/main/java/org/spaceworks/app/staff/StaffStateHolder.kt b/app/src/main/java/org/spaceworks/app/staff/StaffStateHolder.kt index 9dc902d..59168fb 100644 --- a/app/src/main/java/org/spaceworks/app/staff/StaffStateHolder.kt +++ b/app/src/main/java/org/spaceworks/app/staff/StaffStateHolder.kt @@ -391,7 +391,7 @@ class StaffStateHolder( fun canConfirmAttendance()=canRequest()&&can("manage_events")&&state.value.person?.let{it.status=="registered"&&it.confirmable&&it.registrationId>0}==true suspend fun confirmAttendance(){if(!canConfirmAttendance())return;val person=state.value.person?:return;val version=checkInVersion mutate("manage_events",{api.markAttended(it,person.registrationId,EventAttendanceMark(source=EventAttendanceMarkSourceEnum.entries.first{it.value=="qr"}))}){row-> - if(version==checkInVersion){if(row.id!=person.registrationId||row.status.value!="attended")throw validation(text(StaffStrings.STAFF_ATTENDANCE_COULD_NOT_BE_CONFIRMED_SCAN_AGAIN_TO_CHECK_THE_LA));mutable.update{it.copy(person=null,attendanceNotice=text(StaffStrings.STAFF_ATTENDANCE_RECORDED_FOR_1_S, person.name))}}} + if(version==checkInVersion){if(row.registrationId!=person.registrationId||row.attendanceStatus!="attended")throw validation(text(StaffStrings.STAFF_ATTENDANCE_COULD_NOT_BE_CONFIRMED_SCAN_AGAIN_TO_CHECK_THE_LA));mutable.update{it.copy(person=null,attendanceNotice=text(StaffStrings.STAFF_ATTENDANCE_RECORDED_FOR_1_S, person.name))}}} if(state.value.error!=null)mutable.update{it.copy(person=null)} } private fun invalid(detail:String){mutable.update{it.copy(error=detail)}} diff --git a/app/src/main/java/org/spaceworks/app/ui/AccountFlowScreen.kt b/app/src/main/java/org/spaceworks/app/ui/AccountFlowScreen.kt index 5ff3e80..d2b4f2b 100644 --- a/app/src/main/java/org/spaceworks/app/ui/AccountFlowScreen.kt +++ b/app/src/main/java/org/spaceworks/app/ui/AccountFlowScreen.kt @@ -43,8 +43,8 @@ import org.spaceworks.app.auth.* } } BackHandler {if(!state.busy)holder.back()} - val title=when(state.route){AccountRoute.SIGN_UP->copyText(UiStrings.MOBILE_CREATE_YOUR_ACCOUNT);AccountRoute.SIGNUP_ACK->copyText(UiStrings.MOBILE_ACCOUNT_CREATED);AccountRoute.FORGOT_PASSWORD->copyText(UiStrings.MOBILE_RESET_PASSWORD);AccountRoute.RESET_PASSWORD->copyText(UiStrings.MOBILE_ENTER_YOUR_RECOVERY_CODE);AccountRoute.RESET_SUCCESS->copyText(UiStrings.MOBILE_PASSWORD_UPDATED);AccountRoute.RECOVERY_UNAVAILABLE->copyText(UiStrings.MOBILE_PASSWORD_RECOVERY_UNAVAILABLE);else->copyText(UiStrings.MOBILE_CONFIRM_YOUR_EMAIL)} - val subtitle=when(state.route){AccountRoute.SIGN_UP->copyText(UiStrings.MOBILE_USE_THE_EMAIL_YOU_WANT_TO_USE_WITH_YOUR_MAKERSPACE);AccountRoute.SIGNUP_ACK->copyText(UiStrings.MOBILE_SIGN_IN_TO_CONTINUE_YOU_MAY_NEED_TO_CONFIRM_YOUR_EMAIL);AccountRoute.FORGOT_PASSWORD->copyText(UiStrings.MOBILE_ENTER_YOUR_ACCOUNT_EMAIL_TO_REQUEST_A_SIX_DIGIT_RECOVERY_CODE);AccountRoute.RESET_PASSWORD->copyText(UiStrings.MOBILE_IF_AN_ACCOUNT_MATCHES_THIS_EMAIL_A_RECOVERY_CODE_WILL_ARRIVE_DEL);AccountRoute.RESET_SUCCESS->copyText(UiStrings.MOBILE_YOUR_PASSWORD_HAS_BEEN_UPDATED_YOU_CAN_NOW_SIGN_IN);AccountRoute.RECOVERY_UNAVAILABLE->copyText(UiStrings.MOBILE_CONTACT_YOUR_MAKERSPACE_STAFF_TO_RESET_YOUR_PASSWORD);else->copyText(UiStrings.MOBILE_ENTER_THE_SIX_DIGIT_CODE_SENT_TO_YOUR_EMAIL)} + val title=when(state.route){AccountRoute.SIGN_UP->copyText(UiStrings.MOBILE_CREATE_YOUR_ACCOUNT);AccountRoute.SIGNUP_ACK->copyText(UiStrings.MOBILE_ACCOUNT_CREATED);AccountRoute.FORGOT_PASSWORD->copyText(UiStrings.MOBILE_RESET_PASSWORD);AccountRoute.RESET_PASSWORD->copyText(UiStrings.MOBILE_ENTER_YOUR_RECOVERY_CODE);AccountRoute.RESET_SUCCESS->copyText(UiStrings.MOBILE_PASSWORD_UPDATED);AccountRoute.RECOVERY_UNAVAILABLE->copyText(UiStrings.MOBILE_PASSWORD_RECOVERY_UNAVAILABLE);AccountRoute.RESTORE_PHONE,AccountRoute.RESTORE_CODE->copyText(UiStrings.MOBILE_RESTORE_A_DELETED_ACCOUNT);AccountRoute.RESTORE_SUCCESS->copyText(UiStrings.MOBILE_ACCOUNT_RESTORED);else->copyText(UiStrings.MOBILE_CONFIRM_YOUR_EMAIL)} + val subtitle=when(state.route){AccountRoute.SIGN_UP->copyText(UiStrings.MOBILE_USE_THE_EMAIL_YOU_WANT_TO_USE_WITH_YOUR_MAKERSPACE);AccountRoute.SIGNUP_ACK->copyText(UiStrings.MOBILE_SIGN_IN_TO_CONTINUE_YOU_MAY_NEED_TO_CONFIRM_YOUR_EMAIL);AccountRoute.FORGOT_PASSWORD->copyText(UiStrings.MOBILE_ENTER_YOUR_ACCOUNT_EMAIL_TO_REQUEST_A_SIX_DIGIT_RECOVERY_CODE);AccountRoute.RESET_PASSWORD->copyText(UiStrings.MOBILE_IF_AN_ACCOUNT_MATCHES_THIS_EMAIL_A_RECOVERY_CODE_WILL_ARRIVE_DEL);AccountRoute.RESET_SUCCESS->copyText(UiStrings.MOBILE_YOUR_PASSWORD_HAS_BEEN_UPDATED_YOU_CAN_NOW_SIGN_IN);AccountRoute.RECOVERY_UNAVAILABLE->copyText(UiStrings.MOBILE_CONTACT_YOUR_MAKERSPACE_STAFF_TO_RESET_YOUR_PASSWORD);AccountRoute.RESTORE_PHONE->copyText(UiStrings.MOBILE_RESTORE_WITH_PHONE_HELP);AccountRoute.RESTORE_CODE->copyText(UiStrings.MOBILE_RESTORE_SET_PASSWORD_HELP);AccountRoute.RESTORE_SUCCESS->copyText(UiStrings.MOBILE_RESTORED_SIGN_IN_AGAIN);else->copyText(UiStrings.MOBILE_ENTER_THE_SIX_DIGIT_CODE_SENT_TO_YOUR_EMAIL)} AuthLayout(title,subtitle) { when(state.route) { AccountRoute.SIGN_UP -> { @@ -77,7 +77,22 @@ import org.spaceworks.app.auth.* val seconds=((state.resendAtMillis-now+999)/1000).coerceAtLeast(0) BrandTextButton(if(seconds>0)copyText(UiStrings.MOBILE_SEND_ANOTHER_CODE_IN_VALUES,seconds) else copyText(UiStrings.MOBILE_SEND_ANOTHER_CODE),{submit {holder.resendVerification()}},ready && seconds==0L) } - AccountRoute.SIGNUP_ACK,AccountRoute.RESET_SUCCESS -> BrandButton(copyText(UiStrings.MOBILE_GO_TO_SIGN_IN),holder::back) + AccountRoute.RESTORE_PHONE -> { + BrandField(phone,{phone=it.filter {c->c.isDigit() || c=='+' || c==' '}.take(20)},copyText(UiStrings.MOBILE_PHONE_NUMBER),Modifier.testTag("restorePhone"),!state.busy,role=FieldRole.PHONE, + onDone={if(ready)submit {holder.restoreStart(phone)}}) + BrandButton(if(state.busy)copyText(UiStrings.MOBILE_SENDING) else copyText(UiStrings.MOBILE_SEND_CODE),{submit {holder.restoreStart(phone)}},ready && phone.any {it.isDigit()},Modifier.testTag("restoreSend")) + } + AccountRoute.RESTORE_CODE -> { + Text(state.phone,style=MaterialTheme.typography.bodyMedium) + BrandField(code,{code=it.filter {c->c in '0'..'9'}.take(6)},copyText(UiStrings.MOBILE_SIX_DIGIT_CODE),Modifier.testTag("restoreCode"),!state.busy,role=FieldRole.ONE_TIME_CODE) + BrandField(password,{password=it.take(128)},copyText(UiStrings.MOBILE_NEW_PASSWORD),Modifier.testTag("restorePassword"),!state.busy,secret=true,role=FieldRole.NEW_PASSWORD) + BrandField(confirmation,{confirmation=it.take(128)},copyText(UiStrings.MOBILE_CONFIRM_PASSWORD),Modifier.testTag("restoreConfirmation"),!state.busy,secret=true,role=FieldRole.NEW_PASSWORD,onDone={if(ready)submit {holder.restore(code,password,confirmation)}}) + BrandButton(if(state.busy)copyText(UiStrings.MOBILE_UPDATING) else copyText(UiStrings.MOBILE_RESTORE_ACCOUNT),{submit {holder.restore(code,password,confirmation)}},ready,Modifier.testTag("restoreSubmit")) + val seconds=((state.resendAtMillis-now+999)/1000).coerceAtLeast(0) + BrandTextButton(if(seconds>0)copyText(UiStrings.MOBILE_RESEND_IN_VALUES,seconds) else copyText(UiStrings.MOBILE_RESEND_CODE),{submit {holder.restoreStart(state.phone,true)}},ready && seconds==0L) + BrandTextButton(copyText(UiStrings.MOBILE_CHANGE_NUMBER),{holder.open(AccountRoute.RESTORE_PHONE)},!state.busy) + } + AccountRoute.SIGNUP_ACK,AccountRoute.RESET_SUCCESS,AccountRoute.RESTORE_SUCCESS -> BrandButton(copyText(UiStrings.MOBILE_GO_TO_SIGN_IN),holder::back) AccountRoute.RECOVERY_UNAVAILABLE -> Unit null -> Unit } @@ -86,6 +101,6 @@ import org.spaceworks.app.auth.* state.notice?.let {Text(it)} if(now AuthLayout(copyText(UiStrings.MOBILE_UPDATE_REQUIRED),copyText(UiStrings.MOBILE_THIS_VERSION_IS_NO_LONGER_SUPPORTED_BY_YOUR_SERVER)) { + BrandButton(copyText(UiStrings.MOBILE_OPEN_GOOGLE_PLAY),{ + val id=context.packageName + runCatching {context.startActivity(android.content.Intent(android.content.Intent.ACTION_VIEW,android.net.Uri.parse("market://details?id=$id")))} + .recoverCatching {context.startActivity(android.content.Intent(android.content.Intent.ACTION_VIEW,android.net.Uri.parse("https://play.google.com/store/apps/details?id=$id")))} + },!state.busy) + BrandTextButton(copyText(UiStrings.MOBILE_TRY_AGAIN),{scope.launch {holder.initialize()}},!state.busy) + onChangeServer?.let {BrandTextButton(copyText(UiStrings.MOBILE_CHANGE_SERVER),it,!state.busy)} + } state.gate!=null -> AuthLayout(if(state.gate==AuthGate.WEB_CONSOLE)copyText(UiStrings.MOBILE_USE_THE_WEB_CONSOLE) else copyText(UiStrings.MOBILE_SIGN_IN_AGAIN), if(state.gate==AuthGate.WEB_CONSOLE)copyText(UiStrings.MOBILE_SUPERADMIN_ACCOUNTS_USE_THE_WEB_CONSOLE_SIGN_IN_HERE_WITH_A_MEMB) else copyText(UiStrings.MOBILE_YOUR_SESSION_ENDED_SIGN_IN_AGAIN_TO_CONTINUE)) { @@ -132,6 +143,7 @@ import org.spaceworks.app.platform.GoogleIdentity BrandTextButton(copyText(UiStrings.MOBILE_BACK_TO_SIGN_IN),{scope.launch {holder.cancelMfa()}},!state.busy) } } + state.phone!=null -> PhoneSignInScreen(holder,state,retrySeconds,::launchAuth) else -> AuthLayout(copyText(UiStrings.MOBILE_WELCOME_BACK),copyText(UiStrings.MOBILE_SIGN_IN_TO_YOUR_MAKERSPACE_ACCOUNT)) { if(!state.initialized) { if(state.busy) {LinearProgressIndicator(Modifier.fillMaxWidth());Text(copyText(UiStrings.MOBILE_CHECKING_SIGN_IN_METHODS),Modifier.semantics {liveRegion=LiveRegionMode.Polite})} @@ -145,9 +157,21 @@ import org.spaceworks.app.platform.GoogleIdentity else if(!bundle.demo)Text(copyText(UiStrings.MOBILE_GOOGLE_DEVICE_SIGN_IN_IS_NOT_CONFIGURED_FOR_THIS_BUILD_CONTACT_Y),style=MaterialTheme.typography.bodySmall) if(state.passwordEnabled && holder.nativeSocialReady)Text(copyText(UiStrings.MOBILE_OR_SIGN_IN_WITH_A_PASSWORD),style=MaterialTheme.typography.labelLarge) } + if(holder.nativeSocialReady && !bundle.demo)holder.oidcProviders.forEach {provider-> + BrandButton(copyText(UiStrings.MOBILE_CONTINUE_WITH_VALUE,provider.displayName),{ + launchAuth {outcome->holder.oidcSignIn(provider,{p,nonce-> + org.spaceworks.app.platform.OidcBrowser().idToken(context.activity()?:throw AuthMessage(copyText(UiStrings.MOBILE_THIS_SIGN_IN_OPTION_ISN_T_AVAILABLE_ON_THIS_DEVICE)), + p.issuer,p.clientId,nonce,org.spaceworks.app.platform.OidcRedirects.redirectUri(context.packageName)) + },outcome)} + },!state.busy && retrySeconds==0L,Modifier.testTag("oidc-${provider.key}")) + } + if(state.phoneEnabled && holder.nativeSocialReady && !bundle.demo) + BrandButton(copyText(UiStrings.MOBILE_CONTINUE_WITH_PHONE),holder::openPhone,!state.busy && retrySeconds==0L,Modifier.testTag("phoneSignIn")) if(state.passwordEnabled) { BrandField(username,{username=it.take(254);usernameError=null},copyText(UiStrings.MOBILE_EMAIL_OR_USERNAME),Modifier.testTag("username").focusRequester(usernameFocus),!state.busy,role=FieldRole.IDENTITY,errorMessage=usernameError) BrandField(password,{password=it.take(1024);passwordError=null},copyText(UiStrings.MOBILE_PASSWORD),Modifier.testTag("password").focusRequester(passwordFocus),!state.busy,secret=true,onDone={if(!state.busy && retrySeconds==0L)submit()},errorMessage=passwordError) + if(state.phoneEnabled && holder.nativeSocialReady && !bundle.demo)bundle.accounts?.let {accounts-> + BrandTextButton(copyText(UiStrings.MOBILE_RESTORE_A_DELETED_ACCOUNT),{accounts.open(AccountRoute.RESTORE_PHONE)},!state.busy,Modifier.testTag("restoreEntry"))} bundle.accounts?.let {accounts->BrandTextButton(copyText(UiStrings.MOBILE_FORGOT_PASSWORD),{accounts.open(AccountRoute.FORGOT_PASSWORD,username.takeIf {it.contains('@')}.orEmpty())},!state.busy)} BrandError(state.error) state.notice?.let {Text(it)} @@ -158,7 +182,7 @@ import org.spaceworks.app.platform.GoogleIdentity Text(copyText(UiStrings.MOBILE_NEW_HERE),style=MaterialTheme.typography.bodySmall) BrandTextButton(copyText(UiStrings.MOBILE_CREATE_ACCOUNT),{accounts.open(AccountRoute.SIGN_UP)},!state.busy) } - } else if(state.googleClientId==null || !holder.nativeSocialReady) { + } else if((state.googleClientId==null && !state.phoneEnabled && holder.oidcProviders.isEmpty()) || !holder.nativeSocialReady) { Text(copyText(UiStrings.MOBILE_SIGN_IN_ISN_T_AVAILABLE_HERE),style=MaterialTheme.typography.titleMedium) Text(copyText(UiStrings.MOBILE_ASK_YOUR_MAKERSPACE_FOR_HELP_OR_CHECK_THE_SERVER_ADDRESS)) BrandButton(copyText(UiStrings.MOBILE_TRY_AGAIN),{scope.launch {holder.initialize()}},!state.busy) @@ -178,3 +202,46 @@ import org.spaceworks.app.platform.GoogleIdentity } } } + +/** Member-only phone sign-in: send a code, then verify it against the same attested challenge. */ +@Composable private fun PhoneSignInScreen(holder:AuthStateHolder,state:AuthState,retrySeconds:Long, + launchAuth:(suspend((AuthOperationOutcome)->Unit)->Unit)->Unit) { + val copyText=rememberMobileText() + val phone=requireNotNull(state.phone) + val focus=LocalFocusManager.current + val keyboard=LocalSoftwareKeyboardController.current + var country by rememberSaveable {mutableStateOf("+91")} + var number by rememberSaveable {mutableStateOf("")} + var code by rememberSaveable {mutableStateOf("")} + var countries by remember {mutableStateOf(false)} + var now by remember {mutableLongStateOf(System.currentTimeMillis())} + LaunchedEffect(phone.resendAtMillis) {while(System.currentTimeMillis()holder.sendPhoneCode(full,outcome)}} + if(!phone.codeSent) AuthLayout(copyText(UiStrings.MOBILE_CONTINUE_WITH_PHONE),copyText(UiStrings.MOBILE_WE_LL_TEXT_A_SIX_DIGIT_CODE_TO_THIS_NUMBER)) { + Box { + BrandTextButton(copyText(UiStrings.MOBILE_COUNTRY_CODE_VALUE,country),{countries=true},!state.busy) + DropdownMenu(countries,{countries=false}) { + listOf(copyText(UiStrings.MOBILE_INDIA_91) to "+91",copyText(UiStrings.MOBILE_UNITED_STATES_1) to "+1",copyText(UiStrings.MOBILE_UNITED_KINGDOM_44) to "+44") + .forEach {(label,value)->DropdownMenuItem(text={Text(label)},onClick={country=value;countries=false})} + } + } + BrandField(number,{number=it.filter {c->c.isDigit() || c=='+' || c==' '}.take(20)},copyText(UiStrings.MOBILE_PHONE_NUMBER),Modifier.testTag("phoneNumber"),!state.busy, + role=FieldRole.PHONE,onDone={if(!state.busy && retrySeconds==0L)send()}) + BrandError(state.error) + if(retrySeconds>0)Text(copyText(UiStrings.MOBILE_TRY_AGAIN_IN_VALUES,retrySeconds),style=MaterialTheme.typography.bodySmall) + BrandButton(if(state.busy)copyText(UiStrings.MOBILE_SENDING) else copyText(UiStrings.MOBILE_SEND_CODE),::send,!state.busy && retrySeconds==0L && number.any {it.isDigit()},Modifier.testTag("sendPhoneCode")) + BrandTextButton(copyText(UiStrings.MOBILE_USE_ANOTHER_SIGN_IN_METHOD),holder::closePhone,!state.busy) + } else AuthLayout(copyText(UiStrings.MOBILE_ENTER_YOUR_VERIFICATION_CODE),phone.phone) { + BrandField(code,{code=it.filter {c->c in '0'..'9'}.take(6)},copyText(UiStrings.MOBILE_VERIFICATION_CODE),Modifier.testTag("phoneCode"),!state.busy, + role=FieldRole.ONE_TIME_CODE,errorMessage=state.error,onDone={if(code.length==6 && !state.busy){focus.clearFocus();keyboard?.hide();launchAuth {outcome->holder.verifyPhoneCode(code,outcome);code=""}}}) + BrandButton(if(state.busy)copyText(UiStrings.MOBILE_VERIFYING) else copyText(UiStrings.MOBILE_VERIFY),{ + focus.clearFocus();keyboard?.hide();launchAuth {outcome->holder.verifyPhoneCode(code,outcome);code=""} + },!state.busy && code.length==6,Modifier.testTag("verifyPhoneCode")) + val seconds=((phone.resendAtMillis-now+999)/1000).coerceAtLeast(0) + BrandTextButton(if(seconds>0)copyText(UiStrings.MOBILE_RESEND_IN_VALUES,seconds) else copyText(UiStrings.MOBILE_RESEND_CODE), + {launchAuth {outcome->holder.sendPhoneCode(phone.phone,outcome)}},!state.busy && seconds==0L && retrySeconds==0L) + BrandTextButton(copyText(UiStrings.MOBILE_CHANGE_NUMBER),holder::changePhoneNumber,!state.busy) + BrandTextButton(copyText(UiStrings.MOBILE_USE_ANOTHER_SIGN_IN_METHOD),holder::closePhone,!state.busy) + } +} diff --git a/app/src/main/java/org/spaceworks/app/ui/MemberScreens.kt b/app/src/main/java/org/spaceworks/app/ui/MemberScreens.kt index c7c9978..f09c4e0 100644 --- a/app/src/main/java/org/spaceworks/app/ui/MemberScreens.kt +++ b/app/src/main/java/org/spaceworks/app/ui/MemberScreens.kt @@ -38,21 +38,21 @@ fun memberAvailability(item:InventoryItem,text:MobileText=MobileText.ENGLISH):St return if(item.availability=="Ask your makerspace about availability.")text(UiStrings.MOBILE_ASK_YOUR_MAKERSPACE_ABOUT_AVAILABILITY)else memberStatus(item.availability,text) } -@Composable private fun MemberSection(title:String,content:@Composable ColumnScope.()->Unit) { +@Composable internal fun MemberSection(title:String,content:@Composable ColumnScope.()->Unit) { Column(verticalArrangement=Arrangement.spacedBy(12.dp)) { Text(title,style=MaterialTheme.typography.titleLarge,modifier=Modifier.semantics {heading()}) content() } } -@Composable private fun MemberPanel(content:@Composable ColumnScope.()->Unit) { +@Composable internal fun MemberPanel(content:@Composable ColumnScope.()->Unit) { OutlinedCard(Modifier.fillMaxWidth()) {Column(Modifier.padding(20.dp),verticalArrangement=Arrangement.spacedBy(10.dp),content=content)} } -@Composable private fun MemberEmpty(filtered:Boolean=false) { +@Composable internal fun MemberEmpty(filtered:Boolean=false) { val copyText=rememberMobileText() Text(if(filtered)copyText(UiStrings.MOBILE_NO_RESULTS_TRY_ANOTHER_SEARCH) else copyText(UiStrings.MOBILE_NOTHING_HERE_YET),color=MaterialTheme.colorScheme.onSurfaceVariant) } -@Composable private fun MemberLoading() { +@Composable internal fun MemberLoading() { val copyText=rememberMobileText() Column(Modifier.fillMaxWidth().semantics {contentDescription=copyText(UiStrings.MOBILE_LOADING_MAKERSPACE_INFORMATION);liveRegion=LiveRegionMode.Polite},verticalArrangement=Arrangement.spacedBy(12.dp)) { @@ -60,7 +60,7 @@ fun memberAvailability(item:InventoryItem,text:MobileText=MobileText.ENGLISH):St repeat(3){Surface(Modifier.fillMaxWidth().height(56.dp),color=MaterialTheme.colorScheme.surfaceContainerHigh,shape=MaterialTheme.shapes.medium){}} } } -@Composable private fun MemberLoaded(load:MemberLoad,retry:()->Unit,content:@Composable (T)->Unit) { +@Composable internal fun MemberLoaded(load:MemberLoad,retry:()->Unit,content:@Composable (T)->Unit) { val copyText=rememberMobileText() if(load.loading && load.value==null || load.value==null && load.error==null)MemberLoading() @@ -96,7 +96,7 @@ fun memberAvailability(item:InventoryItem,text:MobileText=MobileText.ENGLISH):St } } } -@Composable fun MemberHome(holder:MemberStateHolder,name:String,onInventory:()->Unit,demo:Boolean=false,requestedExtra:Pair?=null,onPayments:(()->Unit)?=null,guideProgress:GuideProgress?=null,bookingsEnabled:Boolean=false,onProfile:(()->Unit)?=null) { +@Composable fun MemberHome(holder:MemberStateHolder,name:String,onInventory:()->Unit,demo:Boolean=false,requestedExtra:Pair?=null,onPayments:(()->Unit)?=null,guideProgress:GuideProgress?=null,bookingsEnabled:Boolean=false,onProfile:(()->Unit)?=null,usage:(@Composable ()->Unit)?=null) { val copyText=rememberMobileText() val state by holder.state.collectAsStateWithLifecycle();val scope=rememberCoroutineScope() @@ -136,6 +136,7 @@ fun memberAvailability(item:InventoryItem,text:MobileText=MobileText.ENGLISH):St MemberSection(copyText(UiStrings.MOBILE_EVENTS)) {if(overview.registrations.isEmpty())MemberEmpty() else overview.registrations.take(3).forEach {MemberRegistrationPanel(it)}} MemberSection(copyText(UiStrings.MOBILE_NOTICES)) {if(overview.notices.isEmpty())MemberEmpty() else overview.notices.forEach {notice->MemberPanel {Text(notice.title,style=MaterialTheme.typography.titleMedium);Text(notice.body)}}} } + usage?.invoke() BrandTextButton(copyText(UiStrings.MOBILE_REFRESH_HOME),{scope.launch {holder.refreshOverview()}},!state.overview.loading) } } diff --git a/app/src/main/java/org/spaceworks/app/ui/MemberShell.kt b/app/src/main/java/org/spaceworks/app/ui/MemberShell.kt index 7fc6f20..416d2d1 100644 --- a/app/src/main/java/org/spaceworks/app/ui/MemberShell.kt +++ b/app/src/main/java/org/spaceworks/app/ui/MemberShell.kt @@ -68,7 +68,13 @@ private val destinationIcons=listOf(Icons.Outlined.Home,Icons.Outlined.Build,Ico val scope=rememberCoroutineScope() var destination by rememberSaveable(state.selectedId){mutableIntStateOf(0)} var signingOut by remember {mutableStateOf(false)} + var signingOutEverywhere by remember {mutableStateOf(false)} var changingServer by remember {mutableStateOf(false)} + if(signingOutEverywhere)AlertDialog(onDismissRequest={signingOutEverywhere=false}, + title={Text(copyText(UiStrings.MOBILE_SIGN_OUT_EVERYWHERE_QUESTION))},text={Text(copyText(UiStrings.MOBILE_SIGN_OUT_EVERYWHERE_HELP))}, + confirmButton={TextButton(onClick={signingOutEverywhere=false;scope.launch {bundle.holder.signOutEverywhere()}}, + colors=ButtonDefaults.textButtonColors(contentColor=MaterialTheme.colorScheme.error)){Text(copyText(UiStrings.MOBILE_SIGN_OUT_EVERYWHERE))}}, + dismissButton={BrandTextButton(copyText(UiStrings.MOBILE_CANCEL),{signingOutEverywhere=false})}) val actions=bundle.actions val actionState=actions?.state?.collectAsStateWithLifecycle()?.value val member=bundle.member @@ -86,6 +92,10 @@ private val destinationIcons=listOf(Icons.Outlined.Home,Icons.Outlined.Build,Ico val payments=bundle.payments val paymentState=payments?.state?.collectAsStateWithLifecycle()?.value var showingPayments by remember(state.selectedId){mutableStateOf(false)} + val insights=bundle.insights + var showingMessaging by remember(state.selectedId){mutableStateOf(false)} + BackHandler(showingMessaging){showingMessaging=false} + LaunchedEffect(insights,state.selectedId) {state.selected?.let {insights?.activate(MemberScope(it.id,it.slug))}} var paymentRoute by remember(state.selectedId){mutableStateOf?>(null)} val nativeCheckout=LocalNativeCheckout.current val nativeState=nativeCheckout?.state?.collectAsStateWithLifecycle()?.value?:NativePaymentState.Idle @@ -181,14 +191,16 @@ private val destinationIcons=listOf(Icons.Outlined.Home,Icons.Outlined.Build,Ico org.spaceworks.app.theme.BrandLinks.privacyURL?.takeIf {org.spaceworks.app.account.AccountLinkPolicy.safe(it)}?.let {url-> BrandTextButton(androidx.compose.ui.res.stringResource(org.spaceworks.app.R.string.account_privacy),{runCatching {context.startActivity(android.content.Intent(android.content.Intent.ACTION_VIEW,android.net.Uri.parse(url)))}})} }) - showingStaff && staffAvailable && staff!=null && (staffState?.scope==null || staffState.scope?.id==state.selectedId) -> org.spaceworks.app.staff.StaffScreen(staff,bundle.demo,{staff.close();showingStaff=false},previewScope=state.selected?.let {MemberScope(it.id,it.slug)}) + showingStaff && staffAvailable && staff!=null && (staffState?.scope==null || staffState.scope?.id==state.selectedId) -> org.spaceworks.app.staff.StaffScreen(staff,bundle.demo,{staff.close();showingStaff=false},previewScope=state.selected?.let {MemberScope(it.id,it.slug)}, + dashboard=insights?.let {holder->{org.spaceworks.app.insights.StaffDashboardPanel(holder)}}) + showingMessaging && insights!=null -> org.spaceworks.app.insights.MessagingConsentScreen(insights,{showingMessaging=false},onAccount) showingStaff -> Text(copyText(UiStrings.MOBILE_LOADING_YOUR_MAKERSPACE),Modifier.padding(24.dp)) showingPayments && paymentState?.scope?.id!=state.selectedId -> Text(copyText(UiStrings.MOBILE_LOADING_YOUR_MAKERSPACE),Modifier.padding(24.dp)) showingPayments && payments!=null -> PaymentsScreen(payments,bundle.demo,{showingPayments=false},nativeState, - launchStripe={input-> + launchCheckout={input-> if(!bundle.demo && !bundle.holder.state.value.busy && bundle.sessionEpoch()==owningEpoch && bundle.holder.state.value.user?.id==owningUser && bundle.holder.state.value.selectedId==paymentState?.scope?.id) - nativeCheckout?.launchStripe?.invoke(input)?:PaymentLaunchResult.HOST_NOT_READY + nativeCheckout?.launchCheckout?.invoke(input)?:PaymentLaunchResult.HOST_NOT_READY else PaymentLaunchResult.HOST_NOT_READY },acknowledgeNative={nativeCheckout?.acknowledge?.invoke()},recoverInterrupted={nativeCheckout?.recoverInterrupted?.invoke()},refreshOnEnter=paymentRoute==null,hostAvailable=!state.busy) actionState?.route!=null && actionState.scope?.id!=state.selectedId -> Text(copyText(UiStrings.MOBILE_LOADING_YOUR_MAKERSPACE),Modifier.padding(24.dp)) @@ -208,6 +220,7 @@ private val destinationIcons=listOf(Icons.Outlined.Home,Icons.Outlined.Build,Ico } actions?.takeIf {actionState?.scope?.id==state.selectedId}?.let {action-> if(actionState?.capabilities?.value?.selfService==true)BrandTextButton(copyText(UiStrings.MOBILE_EDIT_PROFILE),{scope.launch {action.openProfile()}}) + if(actionState?.capabilities?.value?.messaging==true && insights!=null)BrandTextButton(copyText(org.spaceworks.app.insights.InsightsStrings.INSIGHTS_MESSAGES),{showingMessaging=true},!state.busy,modifier=Modifier.semantics {testTag="messaging-entry"}) if(actionState?.capabilities?.value?.memberPreferences==true)BrandTextButton(copyText(UiStrings.MOBILE_COMMUNICATION_PREFERENCES),{scope.launch {action.openPreferences()}}) if(actionState?.capabilities?.loading==true)LinearProgressIndicator(Modifier.fillMaxWidth()) actionState?.capabilities?.error?.let {BrandError(it);BrandTextButton(copyText(UiStrings.MOBILE_RETRY_MAKERSPACE_SETTINGS),{scope.launch {action.refreshCapabilities()}})} @@ -217,12 +230,13 @@ private val destinationIcons=listOf(Icons.Outlined.Home,Icons.Outlined.Build,Ico BrandTextButton(copyText(UsageStrings.USAGE_APP_GUIDE),{showingGuide=true},!state.busy,modifier=Modifier.semantics {testTag="usage-guide-entry"}) onShowIntro?.let {BrandTextButton(copyText(UiStrings.MOBILE_SHOW_INTRODUCTION),it,!state.busy)} BrandButton(copyText(UiStrings.MOBILE_SIGN_OUT),{signingOut=true},!state.busy) + if(!bundle.demo)BrandTextButton(copyText(UiStrings.MOBILE_SIGN_OUT_EVERYWHERE),{signingOutEverywhere=true},!state.busy,modifier=Modifier.semantics {testTag="sign-out-everywhere"}) onChangeServer?.let {BrandTextButton(copyText(UiStrings.MOBILE_CHANGE_SERVER),{changingServer=true},!state.busy)} } } member==null -> Text(copyText(UiStrings.MOBILE_MEMBER_INFORMATION_IS_UNAVAILABLE_FOR_THIS_BUILD),Modifier.padding(24.dp)) content?.scope?.id!=state.selectedId -> Column(Modifier.padding(24.dp)) {LinearProgressIndicator(Modifier.fillMaxWidth());Text(copyText(UiStrings.MOBILE_LOADING_YOUR_MAKERSPACE))} - destination==0 -> MemberHome(member,state.user?.displayName.orEmpty().ifBlank {state.user?.username.orEmpty()},{navigate(1)},bundle.demo,homeExtra,onPayments=payments?.let {{showingPayments=true}},guideProgress=guidance,bookingsEnabled=actionState?.capabilities?.value?.bookableSpaces==true,onProfile={navigate(4)}) + destination==0 -> MemberHome(member,state.user?.displayName.orEmpty().ifBlank {state.user?.username.orEmpty()},{navigate(1)},bundle.demo,homeExtra,onPayments=payments?.let {{showingPayments=true}},guideProgress=guidance,bookingsEnabled=actionState?.capabilities?.value?.bookableSpaces==true,onProfile={navigate(4)},usage=insights?.let {holder->{org.spaceworks.app.insights.MemberUsagePanel(holder)}}) destination==1 -> MemberInventory(member) destination==2 -> MemberActivityScreen(member) else -> MemberInboxScreen(member,notifications={bundle.push?.let {push->NotificationsPanel(push,bundle.demo,bundle.pushConfigured,state.memberships.associate {it.id to makerspaceLabel(it.slug)},bundle.pushPreview){hint->bundle.links?.offer(hint)}}},onOpenUpdate={bundle.links?.offer(it)},navigationSequence=inboxNavigationSerial) diff --git a/app/src/main/java/org/spaceworks/app/ui/UiStrings.kt b/app/src/main/java/org/spaceworks/app/ui/UiStrings.kt index ccb3703..10ecacd 100644 --- a/app/src/main/java/org/spaceworks/app/ui/UiStrings.kt +++ b/app/src/main/java/org/spaceworks/app/ui/UiStrings.kt @@ -449,4 +449,18 @@ object UiStrings { val MOBILE_YOUR_SESSION_ENDED_SIGN_IN_AGAIN_TO_CONTINUE=MobileCopyKey(R.string.mobile_your_session_ended_sign_in_again_to_continue,"Your session ended. Sign in again to continue.") val MOBILE_YOUR_UNSAVED_PROFILE_EDITS_WILL_BE_LOST=MobileCopyKey(R.string.mobile_your_unsaved_profile_edits_will_be_lost,"Your unsaved profile edits will be lost.") val MOBILE_YOUR_VERIFICATION_SESSION_EXPIRED_SIGN_IN_TO_REQUEST_A_NEW_ONE=MobileCopyKey(R.string.mobile_your_verification_session_expired_sign_in_to_request_a_new_one,"Your verification session expired. Sign in to request a new one.") + val MOBILE_WE_LL_TEXT_A_SIX_DIGIT_CODE_TO_THIS_NUMBER=MobileCopyKey(R.string.mobile_we_ll_text_a_six_digit_code_to_this_number,"We'll text a six-digit code to this number.") + val MOBILE_USE_ANOTHER_SIGN_IN_METHOD=MobileCopyKey(R.string.mobile_use_another_sign_in_method,"Use another sign-in method") + val MOBILE_OPEN_GOOGLE_PLAY=MobileCopyKey(R.string.mobile_open_google_play,"Open Google Play") + val MOBILE_THIS_VERSION_IS_NO_LONGER_SUPPORTED_BY_YOUR_SERVER=MobileCopyKey(R.string.mobile_this_version_is_no_longer_supported_by_your_server,"This version is no longer supported by your server. Install the latest version to continue.") + val MOBILE_SIGN_OUT_EVERYWHERE=MobileCopyKey(R.string.mobile_sign_out_everywhere,"Sign out everywhere") + val MOBILE_SIGN_OUT_EVERYWHERE_QUESTION=MobileCopyKey(R.string.mobile_sign_out_everywhere_question,"Sign out everywhere?") + val MOBILE_SIGN_OUT_EVERYWHERE_HELP=MobileCopyKey(R.string.mobile_sign_out_everywhere_help,"This ends every Space Works session for your account on this server, including browsers and other devices.") + val MOBILE_CONTINUE_WITH_VALUE=MobileCopyKey(R.string.mobile_continue_with_value,"Continue with %1\$s") + val MOBILE_RESTORE_A_DELETED_ACCOUNT=MobileCopyKey(R.string.mobile_restore_a_deleted_account,"Restore a deleted account") + val MOBILE_RESTORE_WITH_PHONE_HELP=MobileCopyKey(R.string.mobile_restore_with_phone_help,"Enter the phone number on the deleted account. If it can still be restored, we'll text a code.") + val MOBILE_RESTORE_SET_PASSWORD_HELP=MobileCopyKey(R.string.mobile_restore_set_password_help,"Enter the code we sent and choose a new password.") + val MOBILE_RESTORE_ACCOUNT=MobileCopyKey(R.string.mobile_restore_account,"Restore account") + val MOBILE_ACCOUNT_RESTORED=MobileCopyKey(R.string.mobile_account_restored,"Account restored") + val MOBILE_RESTORED_SIGN_IN_AGAIN=MobileCopyKey(R.string.mobile_restored_sign_in_again,"Sign in with your new password. Memberships, sessions and notifications stay off until your makerspace restores them.") } diff --git a/app/src/main/res/values/account_strings.xml b/app/src/main/res/values/account_strings.xml index 7c4290a..1da9616 100644 --- a/app/src/main/res/values/account_strings.xml +++ b/app/src/main/res/values/account_strings.xml @@ -201,4 +201,28 @@ Account access Explore account deletion preview Makerspace %1$d + Delete your whole Space Works account on this server, across every makerspace. + Delete account + Your account was deleted and this device is signed out. + Remaining identity data is removed after %1$s. + Restoring later + Verification failed. Check your password or code. + Enter your current password and the six-digit code from your authenticator app. + Send verification code + We sent a verification code to your verified phone. + Verify with %1$s + Enter your second factor + Enter the six-digit code from your authenticator app. + Enter the verification code we sent you. + This app can\'t use your available verification methods. Delete your account from the web console instead. + This verification method is unavailable. Try another method. + Use another method + Open the web console + Checking your account… + Open problem report + Account access restricted + Last organization governor + Makerspace temporarily unavailable + Enter your current password. + Enter the six-digit code. diff --git a/app/src/main/res/values/core_strings.xml b/app/src/main/res/values/core_strings.xml index 1ee090f..d1a32d9 100644 --- a/app/src/main/res/values/core_strings.xml +++ b/app/src/main/res/values/core_strings.xml @@ -127,4 +127,11 @@ "This changed. Refresh and try again." "Too many requests. Try again later." "The server is unavailable. Try again later." + "Phone sign-in is unavailable on this server." + "This device could not verify the app. Try again." + "Enter your phone number with its country code." + "Enter the six-digit code." + "Your code expired. Send a new code." + "Signed out on every device and browser." + "This sign-in provider is unavailable. Try again." diff --git a/app/src/main/res/values/insights_strings.xml b/app/src/main/res/values/insights_strings.xml new file mode 100644 index 0000000..acc4243 --- /dev/null +++ b/app/src/main/res/values/insights_strings.xml @@ -0,0 +1,59 @@ + + "Your activity" + "Activity details aren't available for this makerspace." + "Loans" + "Borrowed" + "Returned" + "Out now" + "Machine time" + "Hours" + "Most used machines" + "Bookings" + "Events attended" + "Payments" + "Paid" + "Outstanding" + "By month" + "No activity in this period yet." + "%1$s: %2$s %3$s" + "%1$s %2$s" + "Refresh activity" + "Loading activity" + "Today at a glance" + "Handover queue" + "Ready to issue" + "Due back" + "Pickups" + "Nothing needs your attention right now." + "Outstanding payments" + "Reports" + "Refresh dashboard" + "Pending requests" + "Awaiting issue" + "Overdue loans" + "Pending bookings" + "Upcoming bookings" + "Upcoming events" + "Pending event registrations" + "Open problem reports" + "Low stock" + "Pending prints" + "Active prints" + "Prints awaiting collection" + "Maintenance overdue" + "Warranty expiring" + "Stocktakes awaiting approval" + "Pending payments" + "Failed emails" + "Text and WhatsApp messages" + "Choose whether this makerspace can reach you by text message or WhatsApp. App notifications and email are set separately." + "Text messages (SMS)" + "WhatsApp" + "On" + "Off" + "Messages go to %1$s." + "Verify your phone number in Account and security before turning this on." + "This makerspace doesn't send these messages." + "Messages to this number are paused after a STOP reply. Contact your makerspace to resume them." + "Open Account and security" + diff --git a/app/src/main/res/values/mobile_ui_strings.xml b/app/src/main/res/values/mobile_ui_strings.xml index e09b64e..b4e3288 100644 --- a/app/src/main/res/values/mobile_ui_strings.xml +++ b/app/src/main/res/values/mobile_ui_strings.xml @@ -903,4 +903,18 @@ "Your unsaved profile edits will be lost." "Your verification session expired. Sign in to request a new one." + "We'll text a six-digit code to this number." + "Use another sign-in method" + "Open Google Play" + "This version is no longer supported by your server. Install the latest version to continue." + "Sign out everywhere" + "Sign out everywhere?" + "This ends every Space Works session for your account on this server, including browsers and other devices." + "Continue with %1$s" + "Restore a deleted account" + "Enter the phone number on the deleted account. If it can still be restored, we'll text a code." + "Enter the code we sent and choose a new password." + "Restore account" + "Account restored" + "Sign in with your new password. Memberships, sessions and notifications stay off until your makerspace restores them." \ No newline at end of file diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 0000000..0481fd2 --- /dev/null +++ b/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/app/src/test/java/org/spaceworks/app/AccountAuthBoundaryTest.kt b/app/src/test/java/org/spaceworks/app/AccountAuthBoundaryTest.kt index 9a95332..89860bb 100644 --- a/app/src/test/java/org/spaceworks/app/AccountAuthBoundaryTest.kt +++ b/app/src/test/java/org/spaceworks/app/AccountAuthBoundaryTest.kt @@ -11,7 +11,7 @@ import org.spaceworks.app.network.* import org.spaceworks.generated.model.* import java.io.IOException -private const val LOCAL_USER="""{"id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":1,"slug":"fab-lab","role":"member","role_id":1,"role_name":"Member","role_slug":"member","source":"membership","actions":[],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" +private const val LOCAL_USER="""{"session_authority":"member","id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":1,"slug":"fab-lab","role":"member","role_id":1,"role_ids":[1],"role_slugs":["member"],"role_names":["Member"],"role_name":"Member","role_slug":"member","source":"membership","actions":[],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" private class LocalAccountAuth:AuthGateway { var user=Json.decodeFromString(LOCAL_USER) var entered:CompletableDeferred?=null diff --git a/app/src/test/java/org/spaceworks/app/AccountRestoreTest.kt b/app/src/test/java/org/spaceworks/app/AccountRestoreTest.kt new file mode 100644 index 0000000..de93fb5 --- /dev/null +++ b/app/src/test/java/org/spaceworks/app/AccountRestoreTest.kt @@ -0,0 +1,58 @@ +package org.spaceworks.app + +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.* +import okhttp3.mockwebserver.* +import org.junit.Assert.* +import org.junit.Test +import org.spaceworks.app.auth.* +import org.spaceworks.app.network.* +import org.spaceworks.generated.model.* + +private class RestoreFake:AuthGateway { + var started=0;var restored=0 + var result=AccountRestoreResult(true,"Account restored. Sign in with your new password.") + var failure:ApiFailure?=null + override suspend fun config()=PublicConfig(false,100) + override suspend fun login(username:String,password:String)=throw AssertionError() + override suspend fun enroll(challenge:String)=throw AssertionError() + override suspend fun verify(challenge:String,code:String,enrollment:Boolean)=throw AssertionError() + override suspend fun me()=throw AssertionError() + override suspend fun logout() {} + override suspend fun phoneStart(phone:String):PhoneChallenge {started++;return PhoneChallenge("restore-challenge",300)} + override suspend fun restorePhone(phone:String,code:String,newPassword:String,challenge:PhoneChallenge):AccountRestoreResult { + restored++;failure?.let {throw it};assertEquals("restore-challenge",challenge.challenge);assertEquals("+919876543210",phone);return result + } +} + +class AccountRestoreTest { + @Test fun restoreValidatesThenUsesTheStartedChallengeAndNeverSignsIn()=runTest { + val api=RestoreFake();val holder=AccountStateHolder(api,{false}) + holder.open(AccountRoute.RESTORE_PHONE) + holder.restoreStart("98765");assertEquals(0,api.started);assertNotNull(holder.state.value.error) + holder.restoreStart("+91 98765 43210");assertEquals(AccountRoute.RESTORE_CODE,holder.state.value.route);assertEquals("+919876543210",holder.state.value.phone) + holder.restore("123456","short","short");assertEquals(0,api.restored) + holder.restore("123456","new-password","other-password");assertEquals(0,api.restored) + holder.restore("123456","new-password","new-password") + assertEquals(AccountRoute.RESTORE_SUCCESS,holder.state.value.route);assertEquals("Account restored. Sign in with your new password.",holder.state.value.notice) + } + @Test fun serverRefusalKeepsItsReason()=runTest { + val api=RestoreFake().apply {failure=ApiFailure(409,"Purge has already started for this account.",null,null)} + val holder=AccountStateHolder(api,{false});holder.open(AccountRoute.RESTORE_PHONE) + holder.restoreStart("+919876543210");holder.restore("123456","new-password","new-password") + assertEquals(AccountRoute.RESTORE_CODE,holder.state.value.route);assertEquals("Purge has already started for this account.",holder.state.value.error) + } + @Test fun restoreSendsDeviceChallengeAttestationAndNewPassword()=runBlocking { + MockWebServer().use {server-> + val gateway=GeneratedAuthGateway(ApiSession(server.url("/")),"org.example",EnvironmentEnum.development,DevelopmentAttestation(true,true)) + server.enqueue(MockResponse().setBody("""{"restored":true,"detail":"Restored."}""")) + val result=gateway.restorePhone("+919876543210","123456","new-password",PhoneChallenge("c9",300)) + assertTrue(result.restored) + val request=server.takeRequest();assertEquals("/api/v1/auth/account/restore/phone",request.path);assertNull(request.getHeader("Authorization")) + val body=Json.parseToJsonElement(request.body.readUtf8()).jsonObject + assertEquals("c9",body["challenge"]!!.jsonPrimitive.content);assertEquals("device",body["delivery"]!!.jsonPrimitive.content) + assertEquals("new-password",body["new_password"]!!.jsonPrimitive.content);assertEquals("development",body["attestation"]!!.jsonObject["type"]!!.jsonPrimitive.content) + } + } +} diff --git a/app/src/test/java/org/spaceworks/app/AuthEpochTest.kt b/app/src/test/java/org/spaceworks/app/AuthEpochTest.kt index 609676a..3c39678 100644 --- a/app/src/test/java/org/spaceworks/app/AuthEpochTest.kt +++ b/app/src/test/java/org/spaceworks/app/AuthEpochTest.kt @@ -10,7 +10,7 @@ import org.spaceworks.app.auth.* import org.spaceworks.app.network.* import org.spaceworks.generated.model.* -private const val EPOCH_USER="""{"id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":1,"slug":"fab-lab","role":"member","role_id":1,"role_name":"Member","role_slug":"member","source":"membership","actions":[],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" +private const val EPOCH_USER="""{"session_authority":"member","id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":1,"slug":"fab-lab","role":"member","role_id":1,"role_ids":[1],"role_slugs":["member"],"role_names":["Member"],"role_name":"Member","role_slug":"member","source":"membership","actions":[],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" private class EpochAuth:AuthGateway { var entered:CompletableDeferred?=null var release:CompletableDeferred?=null diff --git a/app/src/test/java/org/spaceworks/app/AuthSignalCompletionTest.kt b/app/src/test/java/org/spaceworks/app/AuthSignalCompletionTest.kt index b0aa7da..f5d7d1e 100644 --- a/app/src/test/java/org/spaceworks/app/AuthSignalCompletionTest.kt +++ b/app/src/test/java/org/spaceworks/app/AuthSignalCompletionTest.kt @@ -31,7 +31,7 @@ class AuthSignalCompletionTest { override suspend fun config()=PublicConfig(false,100) override suspend fun me():AuthUserPayload { failure?.let {throw it} - return AuthUserPayload(1,"member","member@example.invalid","Member","",true,"member",false,true,emptyList()) + return AuthUserPayload("member",1,"member","member@example.invalid","Member","",true,"member",false,true,emptyList()) } override suspend fun login(username:String,password:String):DeviceLoginResult=error("Unexpected login") override suspend fun enroll(challenge:String):MFAEnrollment=error("Unexpected enrollment") diff --git a/app/src/test/java/org/spaceworks/app/AuthTest.kt b/app/src/test/java/org/spaceworks/app/AuthTest.kt index 67a1c29..3841eb1 100644 --- a/app/src/test/java/org/spaceworks/app/AuthTest.kt +++ b/app/src/test/java/org/spaceworks/app/AuthTest.kt @@ -12,7 +12,7 @@ import org.spaceworks.app.network.* import org.spaceworks.generated.model.* import java.io.IOException -private const val USER="""{"id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":1,"slug":"fab-lab","role":"member","role_id":1,"role_name":"Member","role_slug":"member","source":"membership","actions":[],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" +private const val USER="""{"session_authority":"member","id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":1,"slug":"fab-lab","role":"member","role_id":1,"role_ids":[1],"role_slugs":["member"],"role_names":["Member"],"role_name":"Member","role_slug":"member","source":"membership","actions":[],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" private class FakeAuth:AuthGateway { var who=Json.decodeFromString(USER) var identityFailure:Exception?=null diff --git a/app/src/test/java/org/spaceworks/app/NativeAuthTest.kt b/app/src/test/java/org/spaceworks/app/NativeAuthTest.kt new file mode 100644 index 0000000..e08fd3f --- /dev/null +++ b/app/src/test/java/org/spaceworks/app/NativeAuthTest.kt @@ -0,0 +1,209 @@ +package org.spaceworks.app + +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.* +import okhttp3.mockwebserver.* +import org.junit.Assert.* +import org.junit.Test +import org.spaceworks.app.auth.* +import org.spaceworks.app.network.* +import org.spaceworks.generated.model.* + +private const val NATIVE_USER="""{"session_authority":"member","id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":1,"slug":"fab-lab","role":"member","role_id":1,"role_ids":[1],"role_slugs":["member"],"role_names":["Member"],"role_name":"Member","role_slug":"member","source":"membership","actions":["issue_hardware"],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" +private const val GRANT="""{"id":"00000000-0000-0000-0000-000000000001","platform":"android","app_id":"org.example","environment":"development","status":"active","attested_at":"2026-10-17T00:00:00Z","last_used_at":"2026-10-17T00:00:00Z","created_at":"2026-10-17T00:00:00Z"}""" + +private fun nativeConfig(password:Boolean=true,phone:Boolean=true,google:String?="android-audience",minimum:String?=null)=PublicConfig(false,100, + native=NativeConfig(NativeMinVersion(minimum,null),NativeProviders(password,phone,google?.let {NativeGoogle(it,"ios-audience")},null,emptyList()))) + +private fun grant():DeviceGrant {val at=java.time.OffsetDateTime.parse("2026-10-17T00:00:00Z") + return DeviceGrant(java.util.UUID.fromString("00000000-0000-0000-0000-000000000001"),"android","org.example","development","active",at,at,at)} +private class NativeFakeAuth:AuthGateway { + var config=nativeConfig() + var who=Json.decodeFromString(NATIVE_USER) + var phoneResult:PhoneLoginResult=PhoneLoginResult.DeviceTokenResponseValue(DeviceTokenResponse("phone-access","phone-refresh",emptyMap(),grant())) + var started=0;var confirmed=0;var loggedOutAll=0;var meCalls=0 + var lastChallenge:PhoneChallenge?=null + var logoutAllFailure:Exception?=null + override val nativeSocialReady=true + override suspend fun config()=config + override suspend fun login(username:String,password:String):DeviceLoginResult=throw AssertionError("unused") + override suspend fun enroll(challenge:String)=throw AssertionError("unused") + override suspend fun verify(challenge:String,code:String,enrollment:Boolean)=throw AssertionError("unused") + override suspend fun me():AuthUserPayload {meCalls++;return who} + override suspend fun logout() {} + override suspend fun phoneStart(phone:String):PhoneChallenge {started++;return PhoneChallenge("phone-challenge-$started",120)} + override suspend fun phoneConfirm(phone:String,code:String,challenge:PhoneChallenge):PhoneLoginResult {confirmed++;lastChallenge=challenge;return phoneResult} + override suspend fun logoutAll(expectedEpoch:Long):LogoutAllResponse {logoutAllFailure?.let {throw it};loggedOutAll++;return LogoutAllResponse(1,2)} +} + +class NativeAuthTest { + @Test fun versionComparisonUsesNumericComponents() { + assertTrue(AppVersion.below("1.9.0","1.10"));assertTrue(AppVersion.below("0.1.0","0.1.1")) + assertFalse(AppVersion.below("1.10.0","1.9.9"));assertFalse(AppVersion.below("2.0","2"));assertFalse(AppVersion.below("2.0.0","1.99")) + assertFalse(AppVersion.below("1.0.0",null));assertFalse(AppVersion.below("1.0.0","beta"));assertFalse(AppVersion.below(null,"9.0")) + } + @Test fun belowMinimumVersionBlocksBeforeRestoringIdentity()=runTest { + val api=NativeFakeAuth().apply {config=nativeConfig(minimum="1.4.0")} + val store=MemoryTokenStore().apply {replace(TokenPair("access","refresh"))} + val holder=AuthStateHolder(api,store,appVersion="1.3.9") + holder.initialize() + assertEquals(AuthGate.UPDATE_REQUIRED,holder.state.value.gate);assertEquals(0,api.meCalls) + assertNotNull(store.read());holder.dismissGate();assertEquals(AuthGate.UPDATE_REQUIRED,holder.state.value.gate) + api.config=nativeConfig(minimum="1.3.9");holder.initialize() + assertNull(holder.state.value.gate);assertEquals(1,holder.state.value.user!!.id) + } + @Test fun nativeProvidersDriveVisibleMethods()=runTest { + val api=NativeFakeAuth().apply {config=nativeConfig(password=false,phone=false,google=" ")} + val holder=AuthStateHolder(api,MemoryTokenStore());holder.initialize() + assertFalse(holder.state.value.passwordEnabled);assertFalse(holder.state.value.phoneEnabled);assertNull(holder.state.value.googleClientId) + api.config=nativeConfig();holder.initialize() + assertTrue(holder.state.value.passwordEnabled);assertTrue(holder.state.value.phoneEnabled);assertEquals("android-audience",holder.state.value.googleClientId) + } + @Test fun staffToolsRequireNativeStaffSessionAuthority()=runTest { + val api=NativeFakeAuth();val store=MemoryTokenStore().apply {replace(TokenPair("access","refresh"))} + val holder=AuthStateHolder(api,store);holder.initialize() + assertFalse(holder.state.value.staffAllowed) + api.who=api.who.copy(sessionAuthority="native_staff");holder.foreground() + assertTrue(holder.state.value.staffAllowed) + api.who=api.who.copy(sessionAuthority="staff");holder.foreground() + assertFalse(holder.state.value.staffAllowed) + } + @Test fun phoneSignInReusesTheStartedChallengeAndInstallsDeviceTokens()=runTest { + var clock=1_000L + val api=NativeFakeAuth();val store=MemoryTokenStore() + val holder=AuthStateHolder(api,store,now={clock});holder.initialize() + holder.openPhone();holder.sendPhoneCode("12345") + assertEquals(0,api.started);assertNotNull(holder.state.value.error) + holder.sendPhoneCode("+91 98765 43210") + assertEquals("+919876543210",holder.state.value.phone!!.phone);assertTrue(holder.state.value.phone!!.codeSent) + holder.verifyPhoneCode("12a") + assertEquals(0,api.confirmed) + clock+=5_000;holder.verifyPhoneCode("123456") + assertEquals("phone-challenge-1",api.lastChallenge!!.challenge) + assertEquals(TokenPair("phone-access","phone-refresh"),store.read());assertNull(holder.state.value.phone);assertEquals(1,holder.state.value.user!!.id) + } + @Test fun expiredPhoneCodeAsksForAFreshCodeWithoutCallingTheServer()=runTest { + var clock=1_000L + val api=NativeFakeAuth();val holder=AuthStateHolder(api,MemoryTokenStore(),now={clock});holder.initialize() + holder.openPhone();holder.sendPhoneCode("+447700900123") + clock+=121_000;holder.verifyPhoneCode("123456") + assertEquals(0,api.confirmed);assertFalse(holder.state.value.phone!!.codeSent);assertEquals("Your code expired. Send a new code.",holder.state.value.error) + } + @Test fun phoneSignInCanRequireMfaAndRejectsBrowserOnlyCompletion()=runTest { + val api=NativeFakeAuth().apply {phoneResult=PhoneLoginResult.MFAChallengeResponseValue(MFAChallengeResponse(true,false,"mfa",300))} + val holder=AuthStateHolder(api,MemoryTokenStore());holder.initialize() + holder.openPhone();holder.sendPhoneCode("+919876543210");holder.verifyPhoneCode("123456") + assertEquals("mfa",holder.state.value.mfa!!.challenge);assertNull(holder.state.value.phone) + val second=NativeFakeAuth().apply {phoneResult=PhoneLoginResult.MFACompletedValue(MFACompleted(who,"web-access","member"))} + val store=MemoryTokenStore();val other=AuthStateHolder(second,store);other.initialize() + other.openPhone();other.sendPhoneCode("+919876543210");other.verifyPhoneCode("123456") + assertNull(store.read());assertNull(other.state.value.user);assertNotNull(other.state.value.error) + } + @Test fun signOutEverywhereClearsOnlyAfterServerConfirmation()=runTest { + val api=NativeFakeAuth();val store=MemoryTokenStore().apply {replace(TokenPair("access","refresh"))} + val holder=AuthStateHolder(api,store);holder.initialize() + api.logoutAllFailure=ApiFailure(503,"Unavailable",null,null);holder.signOutEverywhere() + assertNotNull(store.read());assertNotNull(holder.state.value.user) + api.logoutAllFailure=null;holder.signOutEverywhere() + assertNull(store.read());assertNull(holder.state.value.user);assertEquals(1,api.loggedOutAll) + assertEquals("Signed out on every device and browser.",holder.state.value.notice) + } + + @Test fun phoneStartAndConfirmSendDeviceDeliveryAndPlayIntegrityProof()=runBlocking { + MockWebServer().use {server-> + var hashed:String?=null + val attestation=NativeAttestation(DevelopmentAttestation(false,false),{challenge->hashed=org.spaceworks.app.platform.PlayIntegrityProofProvider.requestHash(challenge);"integrity-token"}) + val gateway=GeneratedAuthGateway(ApiSession(server.url("/")),"org.example",EnvironmentEnum.production,attestation) + assertTrue(gateway.nativeSocialReady) + server.enqueue(MockResponse().setBody("""{"challenge":"device-challenge","expires_in":90}""")) + server.enqueue(MockResponse().setBody("""{"detail":"If the number can sign in, a code was sent."}""")) + val challenge=gateway.phoneStart("+919876543210") + assertEquals(90,challenge.expiresInSeconds);assertEquals("PhoneChallenge(redacted)",challenge.toString()) + val identity=Json.parseToJsonElement(server.takeRequest().body.readUtf8()).jsonObject + assertEquals("android",identity["platform"]!!.jsonPrimitive.content);assertEquals("production",identity["environment"]!!.jsonPrimitive.content) + val start=server.takeRequest();assertEquals("/api/v1/auth/phone/login/start",start.path) + val startBody=Json.parseToJsonElement(start.body.readUtf8()).jsonObject + assertEquals("device",startBody["delivery"]!!.jsonPrimitive.content);assertEquals("android",startBody["client_platform"]!!.jsonPrimitive.content) + assertEquals("device-challenge",startBody["challenge"]!!.jsonPrimitive.content);assertNull(start.getHeader("Authorization")) + server.enqueue(MockResponse().setBody("""{"access":"a","refresh":"r","user":{},"device_grant":$GRANT}""")) + assertTrue(gateway.phoneConfirm("+919876543210","123456",challenge) is PhoneLoginResult.DeviceTokenResponseValue) + val confirm=server.takeRequest();assertEquals("/api/v1/auth/phone/login/confirm",confirm.path) + val body=Json.parseToJsonElement(confirm.body.readUtf8()).jsonObject + assertEquals("123456",body["code"]!!.jsonPrimitive.content);assertEquals("device-challenge",body["challenge"]!!.jsonPrimitive.content) + assertEquals(buildJsonObject {put("type","play_integrity");put("token","integrity-token")},body["attestation"]) + assertEquals(org.spaceworks.app.platform.PlayIntegrityProofProvider.requestHash("device-challenge"),hashed) + } + } + @Test fun phoneConfirmDecodesMfaChallenge()=runBlocking { + MockWebServer().use {server-> + val gateway=GeneratedAuthGateway(ApiSession(server.url("/")),"org.example",EnvironmentEnum.development,DevelopmentAttestation(true,true)) + server.enqueue(MockResponse().setBody("""{"mfa_required":true,"enrollment_required":false,"challenge":"second","expires_in":300}""")) + val result=gateway.phoneConfirm("+919876543210","123456",PhoneChallenge("c",60)) + assertEquals("second",(result as PhoneLoginResult.MFAChallengeResponseValue).value.challenge) + assertEquals("development",Json.parseToJsonElement(server.takeRequest().body.readUtf8()).jsonObject["attestation"]!!.jsonObject["type"]!!.jsonPrimitive.content) + } + } + @Test fun unconfiguredProductionAttestationIsUnavailableAndFailuresAreReadable()=runBlocking { + val none=NativeAttestation(DevelopmentAttestation(false,false),null) + assertFalse(none.available) + try {none.attest("c");fail()}catch(f:AuthMessage){assertEquals("Device verification is unavailable for this build. Contact your makerspace.",f.message)} + val failing=NativeAttestation(DevelopmentAttestation(false,false),{throw IllegalStateException("provider")}) + try {failing.attest("c");fail()}catch(f:AuthMessage){assertEquals("This device could not verify the app. Try again.",f.message)} + // A Debug emulator always uses the development proof, never Play Integrity. + val debug=NativeAttestation(DevelopmentAttestation(true,true),{throw AssertionError("integrity used")}) + assertTrue(debug.attest("c") is DeviceAttestationPayload.JsonElementValue) + } + @Test fun logoutAllCleansPushThenRevokesEverySessionWithoutMakerspaceHeader()=runBlocking { + MockWebServer().use {server-> + val tokens=MemoryTokenStore().apply {replace(TokenPair("access","refresh"))} + val session=ApiSession(server.url("/"),tokens) {"7"} + var pushCleanups=0;var nativeClears=0 + val gateway=GeneratedAuthGateway(session,"org.example",EnvironmentEnum.development,DevelopmentAttestation(true,true), + onNativeSignOut={nativeClears++},onBeforeOwnedSignOut={pushCleanups++}) + server.enqueue(MockResponse().setBody("""{"web_sessions":2,"devices":3}""")) + val result=gateway.logoutAll(tokens.snapshot().epoch) + assertEquals(3,result.devices);assertEquals(1,pushCleanups);assertEquals(1,nativeClears) + val request=server.takeRequest();assertEquals("/api/v1/auth/logout-all",request.path);assertEquals("POST",request.method) + assertEquals("Bearer access",request.getHeader("Authorization"));assertNull(request.getHeader("X-Makerspace-Id")) + } + } + + @Test fun oidcUsesProviderKeyNonceAndSlugRoute()=runBlocking { + MockWebServer().use {server-> + val gateway=GeneratedAuthGateway(ApiSession(server.url("/")),"org.example",EnvironmentEnum.development,DevelopmentAttestation(true,true)) + server.enqueue(MockResponse().setBody("""{"challenge":"c1","expires_in":60}""")) + server.enqueue(MockResponse().setBody("""{"nonce":"server-nonce","expires_in":300}""")) + server.enqueue(MockResponse().setBody("""{"access":"a","refresh":"r","device_grant":{"id":"00000000-0000-0000-0000-000000000001"},"user":{},"outcome":"authenticated"}""")) + val provider=NativeOidc("oidc:campus","Campus SSO","campus-client","https://sso.example") + val result=gateway.oidc(provider) {p,nonce->assertEquals("campus-client",p.clientId);assertEquals("server-nonce",nonce);"oidc-id-token"} + assertEquals("r",result.refresh) + server.takeRequest() + val nonce=Json.parseToJsonElement(server.takeRequest().body.readUtf8()).jsonObject + assertEquals("oidc:campus",nonce["provider"]!!.jsonPrimitive.content);assertEquals("c1",nonce["challenge"]!!.jsonPrimitive.content) + val login=server.takeRequest();assertEquals("/api/v1/auth/social/oidc/campus",login.path) + val body=Json.parseToJsonElement(login.body.readUtf8()).jsonObject + assertEquals("oidc-id-token",body["id_token"]!!.jsonPrimitive.content);assertEquals("device",body["delivery"]!!.jsonPrimitive.content) + try {gateway.oidc(NativeOidc("google","G","x","https://x")) {_,_->"t"};fail()}catch(_:org.spaceworks.app.platform.MobileCopyException){} + } + } + @Test fun oidcSignInInstallsDeviceSessionOnlyForPublishedProviders()=runTest { + val provider=NativeOidc("oidc:campus","Campus SSO","campus-client","https://sso.example") + val api=object:AuthGateway by NativeFakeAuth() { + val inner=NativeFakeAuth().apply {config=PublicConfig(false,100,native=NativeConfig(NativeMinVersion(null,null),NativeProviders(true,false,null,null,listOf(provider))))} + override val nativeSocialReady=true + override suspend fun config()=inner.config + override suspend fun me()=inner.who + override suspend fun oidc(provider:NativeOidc,credential:suspend(NativeOidc,String)->String):SocialLoginResponse { + assertEquals("id",credential(provider,"n")) + return SocialLoginResponse("oidc-access",emptyMap(),"authenticated","oidc-refresh",mapOf("id" to JsonPrimitive("00000000-0000-0000-0000-000000000001"))) + } + } + val store=MemoryTokenStore();val holder=AuthStateHolder(api,store);holder.initialize() + assertEquals(listOf(provider),holder.oidcProviders) + holder.oidcSignIn(NativeOidc("oidc:other","O","c","https://o"),{_,_->"id"});assertNull(store.read());assertNotNull(holder.state.value.error) + holder.oidcSignIn(provider,{_,_->"id"}) + assertEquals(TokenPair("oidc-access","oidc-refresh"),store.read());assertEquals(1,holder.state.value.user!!.id) + } +} diff --git a/app/src/test/java/org/spaceworks/app/account/AccountDeletionTest.kt b/app/src/test/java/org/spaceworks/app/account/AccountDeletionTest.kt new file mode 100644 index 0000000..06cc6c7 --- /dev/null +++ b/app/src/test/java/org/spaceworks/app/account/AccountDeletionTest.kt @@ -0,0 +1,155 @@ +package org.spaceworks.app.account + +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.* +import okhttp3.mockwebserver.* +import org.junit.Assert.* +import org.junit.Test +import org.spaceworks.app.network.* +import java.util.concurrent.TimeUnit + +private const val BLOCKER="""{"code":"unpaid_charges","makerspace_id":7,"makerspace_slug":"workshop","makerspace_member_url":null,"organization_id":null,"organization_url":null,"count":2,"detail":"Two charges need payment."}""" +private const val ME="""{"session_authority":"member","id":1,"username":"m","email":"m@example.invalid","display_name":"M","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[]}""" + +class AccountDeletionTest { + private fun json(body:String,code:Int=200)=MockResponse().setResponseCode(code).setHeader("Content-Type","application/json").setBody(body) + private fun withGateway(block:suspend(GeneratedAccountDeletionGateway,MockWebServer)->Unit)=runBlocking { + MockWebServer().use {server-> + val tokens=MemoryTokenStore().apply {replace(TokenPair("access","refresh"))} + block(GeneratedAccountDeletionGateway(ApiSession(server.url("/"),tokens) {"7"}) {tokens.read()!=null},server) + } + } + private fun MockWebServer.next()=requireNotNull(takeRequest(5,TimeUnit.SECONDS)) + private fun RecordedRequest.json()=Json.parseToJsonElement(body.readUtf8()).jsonObject + + @Test fun previewProjectsBlockersAndReauthWithoutMakerspaceHeader()=withGateway {gateway,server-> + server.enqueue(json("""{"deletable":false,"restorable":true,"restore_reason":"Restorable for 90 days.","blockers":[$BLOCKER,{"code":"future_code","makerspace_id":null,"makerspace_slug":null,"organization_id":3,"organization_url":"https://example.invalid/org/3","count":1,"detail":"Server says so."}],"reauth":{"required":true,"methods":["password","totp","google"]}}""")) + val preview=gateway.preview() + assertFalse(preview.deletable);assertTrue(preview.reauthRequired);assertEquals(listOf("password","totp","google"),preview.methods) + assertEquals(AccountFixDestination.PAYMENTS,preview.blockers[0].fix);assertEquals(2,preview.blockers[0].count) + assertEquals(AccountFixDestination.SUPPORT,preview.blockers[1].fix);assertEquals("Server says so.",preview.blockers[1].detail) + val request=server.next();assertEquals("GET",request.method);assertEquals("/api/v1/auth/account/deletion",request.path) + assertEquals("Bearer access",request.getHeader("Authorization"));assertNull(request.getHeader("X-Makerspace-Id")) + } + @Test fun passwordProofSendsPasswordAndCodeAndReportsExpiry()=withGateway {gateway,server-> + server.enqueue(json("""{"verified":true,"expires_in":600}""")) + assertEquals(DeletionProof.Verified(600),gateway.password("secret","123456")) + val request=server.next();assertEquals("/api/v1/auth/reauth",request.path) + val body=request.json();assertEquals("secret",body["password"]!!.jsonPrimitive.content);assertEquals("123456",body["code"]!!.jsonPrimitive.content) + } + @Test fun wrongPasswordWithValidIdentityIsAVerificationFailureNotSessionLoss()=withGateway {gateway,server-> + server.enqueue(json("""{"detail":"Identity verification failed."}""",401)) + server.enqueue(json("""{"access":"a2","refresh":"r2","device_grant":{"id":"00000000-0000-0000-0000-000000000001","platform":"android","app_id":"org.example","environment":"development","status":"active","attested_at":"2026-10-17T00:00:00Z","last_used_at":"2026-10-17T00:00:00Z","created_at":"2026-10-17T00:00:00Z"}}""")) + server.enqueue(json("""{"detail":"Identity verification failed."}""",401)) + server.enqueue(json(ME)) + try {gateway.password("wrong",null);fail()}catch(f:ApiFailure){assertEquals("reauth_failed",f.code);assertEquals(0,f.status)} + assertEquals("/api/v1/auth/reauth",server.next().path);assertEquals("/api/v1/auth/device/refresh",server.next().path) + assertEquals("/api/v1/auth/reauth",server.next().path);assertEquals("/api/v1/auth/me",server.next().path) + } + @Test fun phoneAndSocialProofsUseServerTokensAndFactors()=withGateway {gateway,server-> + server.enqueue(json("""{"sent":true,"token":"phone-token"}""")) + val token=gateway.startPhone();assertEquals("PhoneProofToken(redacted)",token.toString()) + assertEquals("/api/v1/auth/reauth/phone/start",server.next().path) + server.enqueue(json("""{"verified":true,"expires_in":300}""")) + assertEquals(DeletionProof.Verified(300),gateway.confirmPhone(token,"654321")) + val confirm=server.next().json();assertEquals("phone-token",confirm["token"]!!.jsonPrimitive.content);assertEquals("654321",confirm["code"]!!.jsonPrimitive.content) + server.enqueue(json("""{"authorization_url":"https://accounts.example/auth","state":"s1","nonce":"n1","expires_in":300}""")) + val intent=gateway.startSocial("google") + assertEquals("google",server.next().json()["provider"]!!.jsonPrimitive.content) + server.enqueue(json("""{"verified":false,"expires_in":0,"mfa_required":true,"factor_required":"totp","factor_token":"factor-1"}""")) + assertEquals(DeletionProof.Factor("factor-1","totp"),gateway.completeSocial(intent,"id-token")) + val complete=server.next().json() + assertEquals("s1",complete["state"]!!.jsonPrimitive.content);assertEquals("n1",complete["nonce"]!!.jsonPrimitive.content);assertEquals("id-token",complete["id_token"]!!.jsonPrimitive.content) + server.enqueue(json("""{"verified":true,"expires_in":600}""")) + assertEquals(DeletionProof.Verified(600),gateway.factor("factor-1","111111")) + assertEquals("/api/v1/auth/reauth/social/factor",server.next().path) + } + @Test fun deletionSendsTypedConfirmationAndDecodesConflicts()=withGateway {gateway,server-> + server.enqueue(json("""{"deleted_at":"2026-10-18T00:00:00Z","purge_after":"2027-01-16T00:00:00Z"}""",202)) + val deleted=gateway.delete() as DeletionOutcome.Deleted + assertTrue(deleted.purgeAfter.startsWith("2027-01-16")) + val request=server.next();assertEquals("POST",request.method);assertEquals(buildJsonObject {put("confirm","DELETE")},request.json()) + server.enqueue(json("""{"blockers":[$BLOCKER]}""",409)) + assertEquals("unpaid_charges",(gateway.delete() as DeletionOutcome.Blocked).blockers.single().code);server.next() + server.enqueue(json("""{"blockers":[$BLOCKER]}""",403)) + assertTrue(gateway.delete() is DeletionOutcome.Blocked);server.next() + server.enqueue(json("""{"detail":"Verify again.","code":"recent_reauth_required"}""",403)) + try {gateway.delete();fail()}catch(f:ApiFailure){assertEquals("recent_reauth_required",f.code)};server.next() + server.enqueue(json("""{"detail":"Try again.","code":"deletion_retry"}""",503)) + try {gateway.delete();fail()}catch(f:ApiFailure){assertEquals(503,f.status)};server.next() + } +} + +private class FakeDeletion:AccountDeletionGateway { + var previews=ArrayDeque() + var passwordResult:DeletionProof=DeletionProof.Verified(600) + var deleteResult:DeletionOutcome=DeletionOutcome.Deleted("2026-10-18T00:00:00Z","2027-01-16T00:00:00Z") + var deleteFailure:ApiFailure?=null + var passwords=0;var deletes=0 + override suspend fun preview()=previews.removeFirstOrNull()?:previews.lastOrNull()?:ready() + override suspend fun password(password:String,code:String?):DeletionProof {passwords++;if(password=="wrong")throw ApiFailure(0,"x","reauth_failed",null);return passwordResult} + override suspend fun startPhone()=PhoneProofToken("t") + override suspend fun confirmPhone(token:PhoneProofToken,code:String)=DeletionProof.Verified(600) + override suspend fun startSocial(provider:String)=SocialProofIntent(provider,"s","n") + override suspend fun completeSocial(intent:SocialProofIntent,idToken:String):DeletionProof {assertEquals("token-for-n",idToken);return DeletionProof.Factor("f","totp")} + override suspend fun factor(token:String,code:String)=DeletionProof.Verified(600) + override suspend fun delete():DeletionOutcome {deletes++;deleteFailure?.let {deleteFailure=null;throw it};return deleteResult} + companion object { + fun ready(methods:List =listOf("password"),required:Boolean=true)=DeletionPreview(true,true,"",emptyList(),required,methods) + val blocked=DeletionPreview(false,false,"",listOf(DeletionBlocker("active_loans",7,"workshop",null,1,"Return hardware.")),true,listOf("password")) + } +} + +class AccountDeletionStateTest { + @Test fun blockersStopBeforeReauthentication()=runTest { + val api=FakeDeletion().apply {previews.add(FakeDeletion.blocked)} + val holder=AccountDeletionStateHolder(api);holder.load() + assertEquals(DeletionStage.BLOCKERS,holder.state.value.stage);assertEquals(AccountFixDestination.ACTIVITY,holder.state.value.preview!!.blockers.single().fix) + holder.verifyPassword("secret","");assertEquals(0,api.passwords) + } + @Test fun passwordProofThenTypedConfirmationDeletes()=runTest { + var deleted:DeletionOutcome.Deleted?=null + val api=FakeDeletion().apply {previews.add(FakeDeletion.ready(listOf("password","totp")))} + val holder=AccountDeletionStateHolder(api,onDeleted={deleted=it});holder.load() + assertEquals(DeletionStage.REAUTHENTICATE,holder.state.value.stage) + holder.verifyPassword("secret","12");assertEquals(DeletionMessage.CODE_REQUIRED,holder.state.value.message);assertEquals(0,api.passwords) + holder.verifyPassword("wrong","123456");assertEquals(DeletionMessage.PROOF_FAILED,holder.state.value.message) + api.previews.add(FakeDeletion.ready(listOf("password","totp"),required=false)) + holder.verifyPassword("secret","123456");assertEquals(DeletionStage.CONFIRM,holder.state.value.stage) + holder.confirm("delete");assertEquals(DeletionMessage.CONFIRM_INVALID,holder.state.value.message);assertEquals(0,api.deletes) + holder.confirm("DELETE");assertEquals(DeletionStage.FINISHED,holder.state.value.stage);assertNotNull(deleted) + } + @Test fun conflictReturnsToFreshBlockersAndExpiredProofReauthenticates()=runTest { + var clock=0L + val api=FakeDeletion().apply {previews.add(FakeDeletion.ready());deleteResult=DeletionOutcome.Blocked(FakeDeletion.blocked.blockers)} + val holder=AccountDeletionStateHolder(api,now={clock});holder.load() + api.previews.add(FakeDeletion.ready(required=false)) + holder.verifyPassword("secret","");holder.confirm("DELETE") + assertEquals(DeletionStage.BLOCKERS,holder.state.value.stage);assertEquals(DeletionMessage.CONFLICT,holder.state.value.message) + api.previews.add(FakeDeletion.ready());holder.load() + api.previews.add(FakeDeletion.ready(required=false));holder.verifyPassword("secret","") + clock+=601_000;holder.confirm("DELETE") + assertEquals(DeletionStage.REAUTHENTICATE,holder.state.value.stage);assertEquals(DeletionMessage.PROOF_EXPIRED,holder.state.value.message);assertEquals(1,api.deletes) + api.previews.add(FakeDeletion.ready(required=false));holder.verifyPassword("secret","") + api.deleteFailure=ApiFailure(403,"Verify again.","recent_reauth_required",null);holder.confirm("DELETE") + assertEquals(DeletionStage.REAUTHENTICATE,holder.state.value.stage) + } + @Test fun socialProofCanRequireASecondFactor()=runTest { + val api=FakeDeletion().apply {previews.add(FakeDeletion.ready(listOf("google")))} + val holder=AccountDeletionStateHolder(api);holder.load() + assertEquals(listOf("google"),holder.state.value.socialProviders) + holder.verifySocial("google") {nonce->"token-for-$nonce"} + assertEquals(DeletionStage.FACTOR,holder.state.value.stage);assertEquals("totp",holder.state.value.factorKind) + api.previews.add(FakeDeletion.ready(listOf("google"),required=false)) + holder.verifyFactor("123456");assertEquals(DeletionStage.CONFIRM,holder.state.value.stage) + } + @Test fun throttlingBlocksFurtherAttemptsUntilRetryAfter()=runTest { + var clock=0L + val api=FakeDeletion().apply {previews.add(FakeDeletion.ready(required=false));deleteFailure=ApiFailure(429,"Wait.","throttled",30)} + val holder=AccountDeletionStateHolder(api,now={clock});holder.load() + holder.confirm("DELETE");assertEquals(30_000L,holder.state.value.retryAtMillis) + holder.confirm("DELETE");assertEquals(1,api.deletes) + clock=30_000;holder.confirm("DELETE");assertEquals(2,api.deletes) + } +} diff --git a/app/src/test/java/org/spaceworks/app/account/AccountGatewayTest.kt b/app/src/test/java/org/spaceworks/app/account/AccountGatewayTest.kt index 04aa45d..586aa82 100644 --- a/app/src/test/java/org/spaceworks/app/account/AccountGatewayTest.kt +++ b/app/src/test/java/org/spaceworks/app/account/AccountGatewayTest.kt @@ -274,5 +274,5 @@ class AccountGatewayTest { assertEquals(0, server.requestCount) } } - private companion object { const val ME = """{"id":1,"username":"meera","email":"meera@example.invalid","display_name":"Meera Nair","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[]}""" } + private companion object { const val ME = """{"session_authority":"member","id":1,"username":"meera","email":"meera@example.invalid","display_name":"Meera Nair","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[]}""" } } diff --git a/app/src/test/java/org/spaceworks/app/account/AccountInstallationIntegrationTest.kt b/app/src/test/java/org/spaceworks/app/account/AccountInstallationIntegrationTest.kt index f3dd933..2838e32 100644 --- a/app/src/test/java/org/spaceworks/app/account/AccountInstallationIntegrationTest.kt +++ b/app/src/test/java/org/spaceworks/app/account/AccountInstallationIntegrationTest.kt @@ -30,7 +30,7 @@ class AccountInstallationIntegrationTest { override suspend fun config()=PublicConfig(false,100) override suspend fun me():AuthUserPayload { reads++;entered?.complete(Unit);release?.await() - return AuthUserPayload(1,"member","member@example.invalid","Member","",true,"member",false,false,emptyList()) + return AuthUserPayload("member",1,"member","member@example.invalid","Member","",true,"member",false,false,emptyList()) } override suspend fun login(username:String,password:String):DeviceLoginResult=error("Unexpected login") override suspend fun enroll(challenge:String):MFAEnrollment=error("Unexpected enrollment") diff --git a/app/src/test/java/org/spaceworks/app/account/LogoutEntryOwnershipTest.kt b/app/src/test/java/org/spaceworks/app/account/LogoutEntryOwnershipTest.kt index 9b7fc42..eca9072 100644 --- a/app/src/test/java/org/spaceworks/app/account/LogoutEntryOwnershipTest.kt +++ b/app/src/test/java/org/spaceworks/app/account/LogoutEntryOwnershipTest.kt @@ -20,7 +20,7 @@ class LogoutEntryOwnershipTest { val session=ApiSession(server.url("/"),tokens);session.bindIdentityEpoch {null} val gateway=GeneratedAuthGateway(session,"org.example",EnvironmentEnum.development,DevelopmentAttestation(true,true)) tokens.replace(TokenPair("new","new-r")) - server.enqueue(MockResponse().setBody(Json.encodeToString(AuthUserPayload(2,"new","new@example.invalid","New","",true,"member",false,false,emptyList())))) + server.enqueue(MockResponse().setBody(Json.encodeToString(AuthUserPayload("member",2,"new","new@example.invalid","New","",true,"member",false,false,emptyList())))) assertTrue(runCatching {gateway.me(expected)}.exceptionOrNull() is java.io.IOException) assertNull(server.takeRequest(200,TimeUnit.MILLISECONDS));assertEquals(TokenPair("new","new-r"),tokens.read()) } @@ -40,7 +40,7 @@ class LogoutEntryOwnershipTest { val gateway=GeneratedAuthGateway(ApiSession(server.url("/"),tokens),"org.example",EnvironmentEnum.development,DevelopmentAttestation(true,true)) val holder=AuthStateHolder(gateway,tokens) server.enqueue(MockResponse().setBody("{\"email_enabled\":false,\"public_image_max_bytes\":100}")) - server.enqueue(MockResponse().setBody(Json.encodeToString(AuthUserPayload(1,"old","old@example.invalid","Old","",true,"member",false,true,emptyList())))) + server.enqueue(MockResponse().setBody(Json.encodeToString(AuthUserPayload("member",1,"old","old@example.invalid","Old","",true,"member",false,true,emptyList())))) holder.initialize();server.takeRequest();server.takeRequest() tokens.replace(TokenPair("new","new-r"));assertTrue(holder.invalidateInstallation(tokens.snapshot().epoch)) server.enqueue(MockResponse().setBody("{\"detail\":\"Password changed\"}")) @@ -62,7 +62,7 @@ class LogoutEntryOwnershipTest { if(!replaced) {replaced=true;tokens.replace(TokenPair("new","new-r"))} }) server.enqueue(MockResponse().setBody("{\"email_enabled\":false,\"public_image_max_bytes\":100}")) - server.enqueue(MockResponse().setBody(Json.encodeToString(AuthUserPayload(1,"old","old@example.invalid","Old","",true,"member",false,forcedPassword,emptyList())))) + server.enqueue(MockResponse().setBody(Json.encodeToString(AuthUserPayload("member",1,"old","old@example.invalid","Old","",true,"member",false,forcedPassword,emptyList())))) holder.initialize() assertNotNull(holder.state.value.user) assertEquals("/api/v1/config",server.takeRequest(2,TimeUnit.SECONDS)!!.path) diff --git a/app/src/test/java/org/spaceworks/app/actions/MakerCapabilitiesTest.kt b/app/src/test/java/org/spaceworks/app/actions/MakerCapabilitiesTest.kt index 4a760c0..f0de379 100644 --- a/app/src/test/java/org/spaceworks/app/actions/MakerCapabilitiesTest.kt +++ b/app/src/test/java/org/spaceworks/app/actions/MakerCapabilitiesTest.kt @@ -11,17 +11,17 @@ import java.util.concurrent.TimeUnit class MakerCapabilitiesTest { private val scope=MemberScope(7,"maker-space") - private fun bootstrap(id:Int=7,modules:String="[\"membership\",\"member_accounts\"]",features:String="[\"membership.self_service\",\"notifications.member_preferences\",\"notifications.digests\"]")="""{ + private fun bootstrap(id:Int=7,messaging:String="{\"sms\":false,\"whatsapp\":false}",modules:String="[\"membership\",\"member_accounts\"]",features:String="[\"membership.self_service\",\"notifications.member_preferences\",\"notifications.digests\"]")="""{ "branding":{},"theme":{},"edition":"community","email_enabled":true,"modules":$modules,"features":$features,"workflows":[], "frontend":{"type":"web","hostname":"example.invalid","allowed_origins":[]}, "public_api":{"base_url":"https://example.invalid/api/v1","inventory_path":"/inventory","publishable_key":"unused"}, - "makerspace":{"id":$id,"name":"Maker space","slug":"maker-space","public_code":"DEMO","location":"Kochi","timezone":"Asia/Kolkata","opening_hours":{},"map_url":"","logo_url":null,"cover_image_url":null,"public_stats_enabled":false,"public_site_enabled":true,"public_inventory_enabled":true,"public_landing_page":"auto","membership_policy":"request"} + "makerspace":{"id":$id,"name":"Maker space","slug":"maker-space","public_code":"DEMO","location":"Kochi","timezone":"Asia/Kolkata","opening_hours":{},"map_url":"","logo_url":null,"cover_image_url":null,"public_stats_enabled":false,"public_site_enabled":true,"public_inventory_enabled":true,"public_landing_page":"auto","membership_policy":"request"},"messaging":$messaging }""" private fun response(body:String)=MockResponse().setHeader("Content-Type","application/json").setBody(body) @Test fun generatedBootstrapUsesPublishedSlugAndImmutableScopeWithoutBrowserCredentials()=runBlocking { MockWebServer().use {server->server.start();server.enqueue(response(bootstrap(modules="[\"membership\",\"member_accounts\",\"bookable_spaces\"]")));val tokens=MemoryTokenStore().apply {replace(TokenPair("access","refresh"))} val result=GeneratedMakerCapabilitiesGateway(ApiSession(server.url("/"),tokens){"999"}).load(scope) - assertEquals(MakerCapabilities.DEMO,result) + assertEquals(MakerCapabilities(true,true,true,true,messaging=false),result) val request=requireNotNull(server.takeRequest(5,TimeUnit.SECONDS));assertEquals("/api/v1/bootstrap?slug=maker-space",request.path);assertEquals("7",request.getHeader("X-Makerspace-Id"));assertEquals("Bearer access",request.getHeader("Authorization")) listOf("Origin","Referer","Cookie","X-Publishable-Key","X-Nonce").forEach {assertNull(request.getHeader(it))} } @@ -47,4 +47,11 @@ class MakerCapabilitiesTest { server.enqueue(response("""{"detail":"Makerspace unavailable."}""").setResponseCode(503));val refused=runCatching {gateway.load(scope)}.exceptionOrNull();assertTrue(refused is ApiFailure);assertEquals("Makerspace unavailable.",refused?.message) } } + @Test fun messagingAvailabilityComesFromPublishedBootstrap()=kotlinx.coroutines.runBlocking { + MockWebServer().use {server->server.start();server.enqueue(response(bootstrap(messaging="{\"sms\":false,\"whatsapp\":true}"))) + val tokens=MemoryTokenStore().apply {replace(TokenPair("access","refresh"))} + val gateway=GeneratedMakerCapabilitiesGateway(ApiSession(server.url("/"),tokens)) + assertTrue(gateway.load(scope).messaging) + } + } } diff --git a/app/src/test/java/org/spaceworks/app/actions/MemberActionsGatewayTest.kt b/app/src/test/java/org/spaceworks/app/actions/MemberActionsGatewayTest.kt index 11c3488..2d1f89d 100644 --- a/app/src/test/java/org/spaceworks/app/actions/MemberActionsGatewayTest.kt +++ b/app/src/test/java/org/spaceworks/app/actions/MemberActionsGatewayTest.kt @@ -301,7 +301,7 @@ class MemberActionsGatewayTest { } private fun availability(rows: String) = """{"public_token":"$SPACE","starts_at":"2026-10-20T04:30:00Z","ends_at":"2026-10-20T05:30:00Z","timezone":"Asia/Kolkata","opening_intervals":[{"starts_at":"2026-10-20T04:00:00Z","ends_at":"2026-10-20T12:00:00Z"}],"availability":$rows}""" - private fun booking(status: String) = """{"public_token":"$BOOKING","space_name":"Studio","timezone":"Asia/Kolkata","starts_at":"2026-10-20T04:30:00Z","ends_at":"2026-10-20T05:30:00Z","status":"$status","cancellation_cutoff_at":"2026-10-19T04:30:00Z","can_cancel":false}""" + private fun booking(status: String) = """{"public_token":"$BOOKING","space_name":"Studio","timezone":"Asia/Kolkata","starts_at":"2026-10-20T04:30:00Z","ends_at":"2026-10-20T05:30:00Z","status":"$status","cancellation_cutoff_at":"2026-10-19T04:30:00Z","can_cancel":false,"confirmation_available":false}""" private fun preferences(enabled: Boolean) = """{"cells":[{"feature":"events","channel":"email","enabled":$enabled,"available":false,"mandatory_events":["account_recovery"]}]}""" companion object { private const val REQUEST = "00000000-0000-0000-0000-000000000081" @@ -310,6 +310,6 @@ class MemberActionsGatewayTest { private const val EVENT = "00000000-0000-0000-0000-000000000084" private const val QUESTION = """{"id":"safety","label":"Read the safety guide?","type":"yes_no","required":true,"options":[]}""" private val SPACE_JSON = """{"public_token":"$SPACE","name":"Studio","kind":"meeting","description":"A quiet room.","capacity":4,"location":"Workshop","image_url":"","approval_mode":"approve","custom_form":[$QUESTION],"show_public_availability":false,"show_public_booker_names":false}""" - private val EVENT_JSON = """{"registration_audience":"invite","category":null,"tags":[],"ticket_types":[{"id":15,"name":"Member ticket","amount":"100.00","audience":"members","spots_left":2}],"promo_codes_enabled":true,"ticket_selection_required":true,"public_token":"$EVENT","title":"Build a shelf","description":"Make together.","starts_at":"2026-10-20T04:30:00Z","ends_at":"2026-10-20T05:30:00Z","location":"Workshop","location_kind":"indoor","custom_form":[$QUESTION],"capacity":12,"payment_amount":"123.45","currency":"INR","availability":"Available","registration_requires_approval":true,"effective_registration_cutoff_at":null,"registration_open":true,"image_url":null,"status":"published","organizers":[],"series":null}""" + private val EVENT_JSON = """{"registration_audience":"invite","category":null,"tags":[],"ticket_types":[{"id":15,"name":"Member ticket","amount":"100.00","audience":"members","spots_left":2}],"promo_codes_enabled":true,"ticket_selection_required":true,"public_token":"$EVENT","title":"Build a shelf","description":"Make together.","starts_at":"2026-10-20T04:30:00Z","ends_at":"2026-10-20T05:30:00Z","location":"Workshop","location_kind":"indoor","custom_form":[$QUESTION],"capacity":12,"payment_amount":"123.45","currency":"INR","availability":"Available","registration_requires_approval":true,"effective_registration_cutoff_at":null,"registration_open":true,"image_url":null,"status":"published","organizers":[],"series":null,"participating_organizations":[],"show_registration_count":false}""" } } diff --git a/app/src/test/java/org/spaceworks/app/insights/InsightsTest.kt b/app/src/test/java/org/spaceworks/app/insights/InsightsTest.kt new file mode 100644 index 0000000..a15bae2 --- /dev/null +++ b/app/src/test/java/org/spaceworks/app/insights/InsightsTest.kt @@ -0,0 +1,116 @@ +package org.spaceworks.app.insights + +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.json.* +import okhttp3.mockwebserver.* +import org.junit.Assert.* +import org.junit.Test +import org.spaceworks.app.member.MemberScope +import org.spaceworks.app.network.* +import java.util.concurrent.TimeUnit + +private const val SPEC="""{"series":["issued_count"],"x":{"field":"period","kind":"time"},"values":[{"field":"issued_count","unit":"loans"}],"currency_field":null,"partition":null,"negatives":"clip","truncation":null,"alt_template":"Loans by month"}""" +private const val HOURS_SPEC="""{"series":["hours"],"x":{"field":"period","kind":"time"},"values":[{"field":"hours","unit":"h"}],"currency_field":null,"partition":"unit","negatives":"clip","truncation":null,"alt_template":"Hours"}""" +private const val TOP_SPEC="""{"series":["hours"],"x":{"field":"machine","kind":"category"},"values":[{"field":"hours","unit":"h"}],"currency_field":null,"partition":null,"negatives":"clip","truncation":{"top_n":5,"other_label":"Other"},"alt_template":"Top"}""" +private const val TABLE_SPEC="""{"series":[],"x":{"field":"currency","kind":"category"},"values":[],"currency_field":"currency","partition":"currency","negatives":"allow","truncation":null,"alt_template":"Totals"}""" +private const val CHANNEL_ON="""{"available":true,"consented":true,"phone_verified":true,"phone_masked":"•••• 3210","suppressed":false}""" +private const val CHANNEL_OFF="""{"available":true,"consented":false,"phone_verified":false,"phone_masked":"","suppressed":false}""" + +class InsightsGatewayTest { + private val scope=MemberScope(7,"workshop") + private fun json(body:String,code:Int=200)=MockResponse().setResponseCode(code).setHeader("Content-Type","application/json").setBody(body) + private fun withGateway(block:suspend(GeneratedInsightsGateway,MockWebServer)->Unit)=runBlocking { + MockWebServer().use {server-> + val tokens=MemoryTokenStore().apply {replace(TokenPair("access","refresh"))} + block(GeneratedInsightsGateway(ApiSession(server.url("/"),tokens)),server) + } + } + private fun MockWebServer.next()=requireNotNull(takeRequest(5,TimeUnit.SECONDS)) + + @Test fun usageKeepsServerNumbersAndOmitsGatedSections()=withGateway {gateway,server-> + server.enqueue(json("""{"usage_available":true, + "loans":{"count":12,"returned_count":10,"currently_out":2,"monthly":{"rows":[{"period":"2026-09","issued_count":4,"return_events":3},{"period":"2026-10","issued_count":"3"},{"issued_count":9}],"chart_spec":$SPEC}}, + "machines":{"hours":"7.50","monthly":{"rows":[{"period":"2026-10","hours":"7.50"}],"chart_spec":$HOURS_SPEC},"top_machines":{"rows":[{"machine_id":1,"machine":"Laser","hours":"4.5"}],"chart_spec":$TOP_SPEC}}, + "payments":{"paid_by_currency":{"INR":"1250.00","USD":"5.00"},"outstanding_by_currency":{},"totals":{"rows":[],"chart_spec":$TABLE_SPEC}}}""")) + val usage=gateway.usage(scope) + assertTrue(usage.available);assertNull(usage.bookings);assertNull(usage.eventsAttended) + assertEquals(12,usage.loans!!.count);assertEquals(2,usage.loans!!.currentlyOut) + // Only rows with both the declared x and first measure are kept; numbers stay as the server sent them. + assertEquals(listOf("2026-09" to "4","2026-10" to "3"),usage.loans!!.monthly.points.map {it.label to it.value}) + assertEquals("loans",usage.loans!!.monthly.unit);assertEquals("7.50",usage.machines!!.hours) + assertEquals("Laser",usage.machines!!.top.points.single().label) + assertEquals(mapOf("INR" to "1250.00","USD" to "5.00"),usage.payments!!.paidByCurrency) + val request=server.next();assertEquals("/api/v1/member/makerspaces/7/my-usage",request.path);assertEquals("7",request.getHeader("X-Makerspace-Id")) + } + @Test fun tableOnlySeriesHasNoPoints() { + val series=GeneratedInsightsGateway.series(org.spaceworks.generated.model.MemberUsageSeries(listOf(mapOf("currency" to JsonPrimitive("INR"))), + Json {ignoreUnknownKeys=true}.decodeFromString(org.spaceworks.generated.model.ReportChartSpec.serializer(),TABLE_SPEC))) + assertTrue(series.points.isEmpty());assertEquals("Totals",series.summary) + } + @Test fun dashboardShowsReportedCountersQueueAndWidgets()=withGateway {gateway,server-> + server.enqueue(json("""{"scope_mode":"full","pending_requests":3,"overdue_loans":0,"outstanding_by_currency":{"INR":"250.00"}, + "widgets":[{"key":"loans","title":"Loans this month","kind":"count","required_action":"view_reports","required_modules":[],"report_keys":["loans"],"default_period":30,"data":{"count":18},"chart_spec":null}, + {"key":"heat","title":"Busy hours","kind":"heatmap","required_action":"view_reports","required_modules":[],"report_keys":[],"default_period":7,"data":{"rows":[]},"chart_spec":null}]}""")) + val dashboard=gateway.dashboard(scope) + assertEquals(StaffScope.FULL,dashboard.scope);assertEquals(listOf(StaffCounter("pending_requests",3)),dashboard.counters) + assertEquals(listOf("18",null),dashboard.widgets.map {it.headline});assertEquals(mapOf("INR" to "250.00"),dashboard.outstandingByCurrency) + assertEquals("/api/v1/admin/makerspace/7/dashboard",server.next().path) + server.enqueue(json("""{"scope_mode":"handover","widgets":[],"queue_tiles":{"ready_to_issue":4,"due_returns":0,"pickups":1}}""")) + val handover=gateway.dashboard(scope) + assertEquals(StaffScope.HANDOVER,handover.scope);assertTrue(handover.counters.isEmpty()) + assertEquals(listOf(StaffCounter("ready_to_issue",4),StaffCounter("due_returns",0),StaffCounter("pickups",1)),handover.queue);server.next() + } + @Test fun messagingReadsScopedSettingsAndWritesExactConsent()=withGateway {gateway,server-> + server.enqueue(json("""{"sms":$CHANNEL_ON,"whatsapp":$CHANNEL_OFF}""")) + val consent=gateway.messaging(scope) + assertTrue(consent.sms.consented);assertFalse(consent.whatsapp.canOptIn);assertEquals("•••• 3210",consent.sms.phoneMasked) + val read=server.next();assertEquals("/api/v1/me/messaging?makerspace=7",read.path);assertEquals("7",read.getHeader("X-Makerspace-Id")) + server.enqueue(json("""{"sms":$CHANNEL_OFF,"whatsapp":$CHANNEL_OFF}""")) + assertFalse(gateway.consent(scope,"sms",false).sms.consented) + val write=server.next();assertEquals("POST",write.method);assertEquals("/api/v1/me/messaging/consent",write.path) + assertEquals(buildJsonObject {put("makerspace",7);put("channel","sms");put("action","opted_out")},Json.parseToJsonElement(write.body.readUtf8())) + server.enqueue(json("""{"detail":"Number suppressed after STOP."}""",409)) + try {gateway.consent(scope,"whatsapp",true);fail()}catch(f:ApiFailure){assertEquals("Number suppressed after STOP.",f.detail)};server.next() + } +} + +private class FakeInsights:InsightsGateway { + var usageCalls=0;var consentCalls=0;var messagingCalls=0 + var consentFailure:ApiFailure?=null + var usageFailure:ApiFailure?=null + var state=MessagingConsent(ConsentChannel(true,false,true,"x",false),ConsentChannel(true,false,false,"",false)) + override suspend fun usage(scope:MemberScope):UsageSummary {usageCalls++;usageFailure?.let {throw it};return UsageSummary(true,null,null,null,scope.id,null)} + override suspend fun dashboard(scope:MemberScope)=StaffDashboard(StaffScope.FULL,emptyList(),emptyList(),emptyList(),emptyMap()) + override suspend fun messaging(scope:MemberScope):MessagingConsent {messagingCalls++;return state} + override suspend fun consent(scope:MemberScope,channel:String,optIn:Boolean):MessagingConsent { + consentCalls++;consentFailure?.let {throw it} + state=state.copy(sms=state.sms.copy(consented=optIn));return state + } +} + +class InsightsStateTest { + @Test fun scopeChangeDiscardsCachedUsage()=runTest { + val api=FakeInsights();val holder=InsightsStateHolder(api) + holder.activate(MemberScope(7,"a"));holder.refreshUsage();assertEquals(7,holder.state.value.usage.value!!.eventsAttended) + holder.activate(MemberScope(8,"b"));assertNull(holder.state.value.usage.value) + holder.refreshUsage();assertEquals(8,holder.state.value.usage.value!!.eventsAttended) + holder.clear();assertNull(holder.state.value.scope) + } + @Test fun consentReflectsServerAndRefetchesAfterRefusal()=runTest { + val api=FakeInsights();val holder=InsightsStateHolder(api);holder.activate(MemberScope(7,"a"));holder.refreshMessaging() + holder.setConsent("whatsapp",true);assertEquals(0,api.consentCalls) + holder.setConsent("sms",true);assertTrue(holder.state.value.messaging.value!!.sms.consented) + api.consentFailure=ApiFailure(409,"Number changed. Verify again.",null,null) + holder.setConsent("sms",false) + assertEquals("Number changed. Verify again.",holder.state.value.consentError);assertEquals(2,api.messagingCalls) + assertTrue(holder.state.value.messaging.value!!.sms.consented);assertNull(holder.state.value.consentBusy) + } + @Test fun throttledUsageWaitsForRetryAfter()=runTest { + var clock=0L + val api=FakeInsights().apply {usageFailure=ApiFailure(429,"Wait.",null,30)} + val holder=InsightsStateHolder(api,now={clock});holder.activate(MemberScope(7,"a")) + holder.refreshUsage();assertEquals("Wait.",holder.state.value.usage.error);holder.refreshUsage();assertEquals(1,api.usageCalls) + clock=30_000;api.usageFailure=null;holder.refreshUsage();assertEquals(2,api.usageCalls);assertNotNull(holder.state.value.usage.value) + } +} diff --git a/app/src/test/java/org/spaceworks/app/member/MemberGatewayTest.kt b/app/src/test/java/org/spaceworks/app/member/MemberGatewayTest.kt index d4e02de..9632c91 100644 --- a/app/src/test/java/org/spaceworks/app/member/MemberGatewayTest.kt +++ b/app/src/test/java/org/spaceworks/app/member/MemberGatewayTest.kt @@ -188,13 +188,13 @@ class MemberGatewayTest { } private fun product(availability: String) = """{"id":19,"name":"Clamp","description":"Useful clamp","category_id":5,"category_name":"Woodwork","category_slug":"wood","borrowing_terms":"Wear eye protection.","availability":$availability,"image_url":"https://images.example/tool.png"}""" - private fun bookingPage() = """{"count":1,"next":"?page=2","previous":null,"results":[{"public_token":"$BOOKING_ID","space_name":"Bench","timezone":"Asia/Kolkata","starts_at":"2026-10-16T10:00:00+05:30","ends_at":"2026-10-16T11:00:00+05:30","status":"confirmed","cancellation_cutoff_at":"2026-10-16T09:00:00+05:30","can_cancel":true}]}""" + private fun bookingPage() = """{"count":1,"next":"?page=2","previous":null,"results":[{"public_token":"$BOOKING_ID","space_name":"Bench","timezone":"Asia/Kolkata","starts_at":"2026-10-16T10:00:00+05:30","ends_at":"2026-10-16T11:00:00+05:30","status":"confirmed","cancellation_cutoff_at":"2026-10-16T09:00:00+05:30","can_cancel":true,"confirmation_available":false}]}""" private fun activity(dues: String = """{"dues_amount":"12.50","outstanding_by_currency":{"INR":"12.50","USD":"2.00"}}""") = """{ "active_hardware_loans":[{"request_id":21,"renewal_count":1,"can_renew":false,"renewal_refusal":"Visit the front desk.","label":"Clamp loan","checked_out_at":"2026-10-01T10:00:00+05:30","due_at":"2026-10-15T10:00:00+05:30","overdue":true}], "recent_presence_sessions":[],"currently_checked_in":false,"accountability":{"membership_active":true,"waiver_acceptance_required":true,"restriction_code":"waiver_required"}, "request_history":[{"status":"pending_review_v2","created_at":"2026-10-01T10:00:00Z","item_count":2,"returned_quantity":0,"damaged_quantity":0,"missing_quantity":0}], "loan_history":[{"label":"Old clamp","checked_out_at":"2026-09-01T10:00:00Z","returned_at":"2026-09-02T10:00:00Z","due_at":null,"returned_late":false}], - "event_registrations":[{"registration_id":31,"checkin_token":null,"event_title":"Make a shelf","starts_at":"2026-10-20T10:00:00Z","ends_at":"2026-10-20T11:00:00Z","status":"new_unknown_state","cancellation_cutoff_at":"2026-10-19T10:00:00Z","can_cancel":false,"waitlist_position":null,"feedback_available":false,"feedback_path":null,"certificate":null}], + "event_registrations":[{"registration_id":31,"checkin_token":null,"event_title":"Make a shelf","starts_at":"2026-10-20T10:00:00Z","ends_at":"2026-10-20T11:00:00Z","status":"new_unknown_state","cancellation_cutoff_at":"2026-10-19T10:00:00Z","can_cancel":false,"confirmation_available":false,"waitlist_position":null,"feedback_available":false,"feedback_path":null,"certificate":null}], "membership_dues":$dues,"membership_term":{"plan_name":"Monthly","starts_at":"2026-10-01T00:00:00Z","ends_at":"2026-11-01T00:00:00Z","current":true,"blocks_borrowing":true}, "notices":[{"level":"warning","event":"loan_due","title":"Loan due","body":"Return your clamp."}], "print_requests":[{"public_token":"$PRINT_ID","status":"queued","title":"Bracket","created_at":"2026-10-01T10:00:00Z","accepted_at":null,"started_at":null,"completed_at":null,"estimated_minutes":30,"queue_position":2,"queue_approved_ahead":1,"queue_awaiting_review_ahead":0}], @@ -208,7 +208,7 @@ class MemberGatewayTest { private const val SPACE_ID = "00000000-0000-0000-0000-000000000073" private const val PRINT_ID = "00000000-0000-0000-0000-000000000074" private val INBOX = """{"id":41,"feature":"hardware_requests","event":"approved","title":"Request approved","summary":"Your request is ready.","link":"https://outside.example/steal","read_at":null,"created_at":"2026-10-01T10:00:00Z"}""" - private val EVENT = """{"registration_audience":"members","category":null,"tags":[],"ticket_types":[],"promo_codes_enabled":false,"ticket_selection_required":false,"public_token":"$EVENT_ID","title":"Make a shelf","description":"Build together.","starts_at":"2026-10-20T10:00:00Z","ends_at":"2026-10-20T11:00:00Z","location":"Workshop","location_kind":"indoor","custom_form":null,"capacity":12,"payment_amount":"123.45","currency":"INR","availability":"Available","registration_requires_approval":false,"effective_registration_cutoff_at":null,"registration_open":true,"image_url":"https://images.example/event.png","status":"published","organizers":[],"series":null}""" + private val EVENT = """{"registration_audience":"members","category":null,"tags":[],"ticket_types":[],"promo_codes_enabled":false,"ticket_selection_required":false,"public_token":"$EVENT_ID","title":"Make a shelf","description":"Build together.","starts_at":"2026-10-20T10:00:00Z","ends_at":"2026-10-20T11:00:00Z","location":"Workshop","location_kind":"indoor","custom_form":null,"capacity":12,"payment_amount":"123.45","currency":"INR","availability":"Available","registration_requires_approval":false,"effective_registration_cutoff_at":null,"registration_open":true,"image_url":"https://images.example/event.png","status":"published","organizers":[],"series":null,"participating_organizations":[],"show_registration_count":false}""" private val SPACE = """{"public_token":"$SPACE_ID","name":"Bench","kind":"bench","description":"A project bench.","capacity":2,"location":"Workshop","image_url":"","approval_mode":"approve","custom_form":null,"show_public_availability":false,"show_public_booker_names":false}""" } } diff --git a/app/src/test/java/org/spaceworks/app/notifications/MobileRoutesTest.kt b/app/src/test/java/org/spaceworks/app/notifications/MobileRoutesTest.kt index 9c4d64d..fccb4bd 100644 --- a/app/src/test/java/org/spaceworks/app/notifications/MobileRoutesTest.kt +++ b/app/src/test/java/org/spaceworks/app/notifications/MobileRoutesTest.kt @@ -25,7 +25,10 @@ class MobileRoutesTest { @Test fun legacyAliasesAreExactAndKeepStaffIntentExplicit(){ assertEquals(LegacyMemberRoute("fab-lab",false),MobileRoutes.legacy("https://makers.example/m/fab-lab/member",server)) assertEquals(LegacyMemberRoute("fab-lab",true),MobileRoutes.legacy("https://makers.example/m/fab-lab/admin/notifications",server)) - for(path in listOf("/m/fab-lab/admin/other","/m/fab-lab/member/history","/m/fab-lab/member/","/m/fab-lab/../member"))assertNull(path,MobileRoutes.legacy("https://makers.example$path",server)) + for(path in listOf("/m/fab-lab/admin/other","/m/fab-lab/admin","/m/fab-lab/member/events/5","/m/fab-lab/events/not-a-uuid","/m/fab-lab/items/0","/m/fab-lab/../member"))assertNull(path,MobileRoutes.legacy("https://makers.example$path",server)) + // Contract tabs and trailing slashes resolve; unknown member tabs open Home. + assertEquals(LegacyMemberRoute("fab-lab",false),MobileRoutes.legacy("https://makers.example/m/fab-lab/member/",server)) + assertEquals(LegacyMemberRoute("fab-lab",false),MobileRoutes.legacy("https://makers.example/m/fab-lab/member/history",server)) assertNull(MobileRoutes.legacy("https://evil.example/m/fab-lab/member",server)) } @Test fun pushVersionAndMismatchedMakerCannotSelectDifferentScope(){ @@ -33,4 +36,22 @@ class MobileRoutesTest { for(version in listOf(0,1,3,99))assertEquals(MobileSection.INBOX,MobileRoutes.hint(PushHint(7,null,null,null,null,"/makerspaces/7/activity",version)).section) assertEquals(MobileDestination(7,MobileSection.INBOX),MobileRoutes.hint(PushHint(7,null,null,null,null,"/makerspaces/8/payments/9",2))) } + + @Test fun serverLinkContractDestinationsMapToNativeSections(){ + val token="7b9e8f2a-1c3d-4e5f-8a9b-0c1d2e3f4a5b" + assertEquals(LegacyMemberRoute("fab-lab",false,MobileSection.PAYMENTS),MobileRoutes.legacyPath("/m/fab-lab/member/payments")) + assertEquals(LegacyMemberRoute("fab-lab",false,MobileSection.ACTIVITY),MobileRoutes.legacyPath("/m/fab-lab/member/loans")) + assertEquals(LegacyMemberRoute("fab-lab",false,MobileSection.PROFILE),MobileRoutes.legacyPath("/m/fab-lab/member/account")) + assertEquals(LegacyMemberRoute("fab-lab",false,MobileSection.REGISTRATION,"31"),MobileRoutes.legacyPath("/m/fab-lab/member/events/31/ticket")) + assertEquals(LegacyMemberRoute("fab-lab",false,MobileSection.EVENTS,token),MobileRoutes.legacyPath("/m/fab-lab/events/$token")) + assertEquals(LegacyMemberRoute("fab-lab",false,MobileSection.BOOKINGS,token),MobileRoutes.legacyPath("/m/fab-lab/bookings/$token/")) + assertEquals(LegacyMemberRoute("fab-lab",false,MobileSection.INVENTORY,"19"),MobileRoutes.legacyPath("/m/fab-lab/items/19")) + for(path in listOf("/control/x","/api/v1/auth/me","/m/fab-lab/admin/requests","/m/fab-lab/events/$token?x=1","/docs"))assertNull(path,MobileRoutes.legacyPath(path)) + } + @Test fun customDomainPushLinksUseTheHintMakerspace(){ + val hint=org.spaceworks.app.platform.PushHint.from(mapOf("v" to "2","makerspace_id" to "7","feature" to "events","event" to "reminder","event_id" to "e1","link" to "/member/events/31/ticket"))!! + assertEquals(MobileDestination(7,MobileSection.REGISTRATION,"31"),MobileRoutes.hint(hint)) + val unsupported=org.spaceworks.app.platform.PushHint.from(mapOf("v" to "2","makerspace_id" to "7","link" to "/m/fab-lab/admin/requests"))!! + assertEquals(MobileDestination(7,MobileSection.INBOX),MobileRoutes.hint(unsupported)) + } } diff --git a/app/src/test/java/org/spaceworks/app/notifications/OwnedPushCleanupTest.kt b/app/src/test/java/org/spaceworks/app/notifications/OwnedPushCleanupTest.kt index 1a3961f..a00e8eb 100644 --- a/app/src/test/java/org/spaceworks/app/notifications/OwnedPushCleanupTest.kt +++ b/app/src/test/java/org/spaceworks/app/notifications/OwnedPushCleanupTest.kt @@ -171,6 +171,6 @@ class OwnedPushCleanupTest { private companion object { fun json(value: String) = MockResponse().setHeader("Content-Type", "application/json").setBody(value) - const val USER = """{"id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":7,"slug":"workshop","role":"member","role_id":1,"role_name":"Member","role_slug":"member","source":"membership","actions":[],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" + const val USER = """{"session_authority":"member","id":1,"username":"member","email":"member@example.invalid","display_name":"Member","phone":"","email_verified":true,"role":"member","is_superuser":false,"must_change_password":false,"makerspaces":[{"id":7,"slug":"workshop","role":"member","role_id":1,"role_ids":[1],"role_slugs":["member"],"role_names":["Member"],"role_name":"Member","role_slug":"member","source":"membership","actions":[],"can_configure_machine_types":false,"is_machine_only":false,"can_refer":false,"can_verify":false,"verified_at":null,"referrals_enabled":false}]}""" } } diff --git a/app/src/test/java/org/spaceworks/app/payments/PaymentsGatewayTest.kt b/app/src/test/java/org/spaceworks/app/payments/PaymentsGatewayTest.kt index e1605b1..0bcaeb7 100644 --- a/app/src/test/java/org/spaceworks/app/payments/PaymentsGatewayTest.kt +++ b/app/src/test/java/org/spaceworks/app/payments/PaymentsGatewayTest.kt @@ -28,11 +28,22 @@ class PaymentsGatewayTest { val request=request(server);assertEquals("GET",request.method);assertEquals("/api/v1/member/makerspaces/7/payments",request.path) }} @Test fun intentUsesOnlyActualChargeIDAndNoInventedVendorPayload()=runBlocking {withGateway{gateway,server,_-> - server.enqueue(json("""{"payment_id":501,"client_secret":"pi_server_secret","publishable_key":"pk_test_server","customer_id":"cus_server","ephemeral_key":"eph_server"}""")) - val intent=gateway.intent(scope,"501");assertEquals("501",intent.paymentId);assertEquals("cus_server",intent.customerId) + server.enqueue(json("""{"vendor":"stripe","payment_id":501,"client_secret":"pi_server_secret","publishable_key":"pk_test_server","customer_id":"cus_server","ephemeral_key":"eph_server"}""")) + val intent=gateway.intent(scope,"501") as PaymentIntent.Stripe;assertEquals("501",intent.paymentId);assertEquals("cus_server",intent.customerId) assertEquals("PaymentIntent(redacted)",intent.toString());val request=request(server) assertEquals("POST",request.method);assertEquals("/api/v1/member/makerspaces/7/payments/501/mobile-intent",request.path);assertEquals(0L,request.bodySize) }} + @Test fun razorpayIntentKeepsServerMinorAmountAndOrder()=runBlocking {withGateway{gateway,server,_-> + server.enqueue(json("""{"vendor":"razorpay","payment_id":501,"key_id":"rzp_test_key","order_id":"order_server","amount_minor":25025,"currency":"INR","name":"Fab Lab","description":"Laser time"}""")) + val intent=gateway.intent(scope,"501") as PaymentIntent.Razorpay + assertEquals("501",intent.paymentId);assertEquals("rzp_test_key",intent.keyId);assertEquals("order_server",intent.orderId) + assertEquals(25025L,intent.amountMinor);assertEquals("INR",intent.currency);assertEquals("Fab Lab",intent.name);assertTrue(intent.valid()) + assertEquals("PaymentIntent(redacted)",intent.toString());assertEquals("/api/v1/member/makerspaces/7/payments/501/mobile-intent",request(server).path) + }} + @Test fun intentWithoutVendorIsRejected()=runBlocking {withGateway{gateway,server,_-> + server.enqueue(json("""{"payment_id":501,"client_secret":"pi_server_secret","publishable_key":"pk_test_server"}""")) + try{gateway.intent(scope,"501");fail("Vendorless intent accepted")}catch(_:Exception){};request(server) + }} @Test fun documentsUseActualPageAndPreserveCreditNoteMoneyAndNextPage()=runBlocking {withGateway{gateway,server,_-> val next=server.url("/api/v1/member/makerspaces/7/payment-documents?page=3") server.enqueue(json("""{"count":2,"next":"$next","previous":null,"results":[$DOCUMENT]}""")) diff --git a/app/src/test/java/org/spaceworks/app/payments/PaymentsStateTest.kt b/app/src/test/java/org/spaceworks/app/payments/PaymentsStateTest.kt index 3db0376..b0b9414 100644 --- a/app/src/test/java/org/spaceworks/app/payments/PaymentsStateTest.kt +++ b/app/src/test/java/org/spaceworks/app/payments/PaymentsStateTest.kt @@ -15,6 +15,15 @@ import org.spaceworks.app.platform.* @OptIn(ExperimentalCoroutinesApi::class) class PaymentsStateTest { + @Test fun razorpayIntentIsValidatedBeforePresentation()=runTest { + val api=FakeGateway().apply{intentBlock={_,_->PaymentIntent.Razorpay("501","rzp_test_key","order_1",25025,"INR","Fab Lab","Laser")}} + val holder=holder(api) + val prepared=holder.prepareCheckout("501")!! + assertTrue(prepared.intent is PaymentIntent.Razorpay);assertEquals(25025L,(prepared.intent as PaymentIntent.Razorpay).amountMinor) + val invalid=FakeGateway().apply{intentBlock={_,_->PaymentIntent.Razorpay("501","pk_wrong","order_1",0,"INR","Fab Lab","")}} + val other=holder(invalid) + assertNull(other.prepareCheckout("501"));assertEquals(PaymentCheckoutPhase.STOPPED,other.state.value.checkout.phase) + } @Test fun supersededPaymentRouteCannotTreatCachedRowsAsItsOwnFreshRead()=runTest { val api=FakeGateway();val holder=holder(api) val firstRead=CompletableDeferred>();val secondRead=CompletableDeferred>() diff --git a/app/src/test/java/org/spaceworks/app/platform/OutcomeFeedbackTest.kt b/app/src/test/java/org/spaceworks/app/platform/OutcomeFeedbackTest.kt index 44978ed..06c192d 100644 --- a/app/src/test/java/org/spaceworks/app/platform/OutcomeFeedbackTest.kt +++ b/app/src/test/java/org/spaceworks/app/platform/OutcomeFeedbackTest.kt @@ -31,7 +31,7 @@ class OutcomeFeedbackTest { } @Test fun mfaAndRequiredPasswordAreProgressionRatherThanCompletedAuthentication() { val before=AuthState(initialized=true) - val user=AuthUserPayload(1,"member","member@example.invalid","Member","",true,"member",false,false,emptyList()) + val user=AuthUserPayload("member",1,"member","member@example.invalid","Member","",true,"member",false,false,emptyList()) assertEquals(AuthOperationKind.PROGRESSED,authOperationKind(before,before.copy(mfa=MFAChallengeResponse(true,false,"challenge",300)),false)) assertEquals(AuthOperationKind.PROGRESSED,authOperationKind(before,before.copy(user=user.copy(mustChangePassword=true)),true)) assertNull(authOperationKind(before,before.copy(user=user),false)) diff --git a/app/src/test/java/org/spaceworks/app/staff/StaffGatewayContractTest.kt b/app/src/test/java/org/spaceworks/app/staff/StaffGatewayContractTest.kt index 975828d..75bfe2a 100644 --- a/app/src/test/java/org/spaceworks/app/staff/StaffGatewayContractTest.kt +++ b/app/src/test/java/org/spaceworks/app/staff/StaffGatewayContractTest.kt @@ -66,7 +66,7 @@ class StaffGatewayContractTest(private val case:StaffApiCase) { StaffApiCase("kit preview","POST","/api/v1/admin/makerspace/7/kit-checklist-preview",{staffFixture("kits")},verify={val body=staffWire(it);assertEquals(JsonPrimitive(601),body["request_id"]);assertEquals(JsonPrimitive("return"),body["phase"])}){assertEquals("72000000-0000-4000-8000-000000000001",it.kitPreview(STAFF_SCOPE,staffBody("""{"phase":"return","request_id":601,"box_code":"CANONICAL-BOX"}""")).components.single().key.toString())}, StaffApiCase("events","GET","/api/v1/admin/makerspaces/7/events/?page=1",{staffPage("event",",\"default_registration_audience\":\"members\"")}){assertEquals(801,it.events(STAFF_SCOPE,1).results.single().id)}, StaffApiCase("check-in lookup only","POST","/api/v1/admin/events/801/check-in/resolve/",{staffFixture("checkin")},verify={assertEquals(JsonPrimitive("OPAQUE-TICKET"),staffWire(it)["checkin_token"])}){assertTrue(it.resolveCheckIn(STAFF_SCOPE,801,EventCheckInResolveRequest("OPAQUE-TICKET")).confirmable)}, - StaffApiCase("attendance separate confirmation","POST","/api/v1/admin/event-registrations/1101/mark-attended/",{staffFixture("registration")},verify={assertEquals(JsonPrimitive("qr"),staffWire(it)["source"])}){assertEquals(1101,it.markAttended(STAFF_SCOPE,1101,staffBody("""{"source":"qr"}""")).id)}, + StaffApiCase("attendance separate confirmation","POST","/api/v1/admin/event-registrations/1101/mark-attended/",{staffFixture("registration")},verify={assertEquals(JsonPrimitive("qr"),staffWire(it)["source"])}){assertEquals(1101,it.markAttended(STAFF_SCOPE,1101,staffBody("""{"source":"qr"}""")).registrationId)}, StaffApiCase("spaces","GET","/api/v1/admin/makerspaces/7/spaces/?page=1",{staffPage("space")}){assertEquals(901,it.spaces(STAFF_SCOPE,1).results.single().id)}, StaffApiCase("bookings","GET","/api/v1/admin/spaces/901/bookings/?page=1",{staffPage("booking")}){assertEquals(901,it.bookings(STAFF_SCOPE,901,1).results.single().spaceId)}, StaffApiCase("approve booking","POST","/api/v1/admin/bookings/1001/approve/",{staffChanged("booking","status",JsonPrimitive("confirmed"))},verify={assertEquals(0L,it.bodySize)}){assertEquals(1001,it.approveBooking(STAFF_SCOPE,1001).id)}, diff --git a/app/src/test/java/org/spaceworks/app/staff/StaffStateTest.kt b/app/src/test/java/org/spaceworks/app/staff/StaffStateTest.kt index cb2dafc..a1ca395 100644 --- a/app/src/test/java/org/spaceworks/app/staff/StaffStateTest.kt +++ b/app/src/test/java/org/spaceworks/app/staff/StaffStateTest.kt @@ -59,7 +59,7 @@ class StaffStateTest { override suspend fun kitPreview(scope:MemberScope,body:KitPreviewInput)=kits override suspend fun events(scope:MemberScope,page:Int):EventListWithDefaults=decode(buildJsonObject{put("count",1);put("default_registration_audience","members");put("results",JsonArray(listOf(fixture("event"))))}) override suspend fun resolveCheckIn(scope:MemberScope,eventId:Int,body:EventCheckInResolveRequest):EventCheckInResolveResponse{checkInReads++;return checkIn} - override suspend fun markAttended(scope:MemberScope,registrationId:Int,body:EventAttendanceMark):EventRegistrationAdmin{marks++;return decode(patch(fixture("registration").jsonObject,"status" to JsonPrimitive("attended")))} + override suspend fun markAttended(scope:MemberScope,registrationId:Int,body:EventAttendanceMark):EventAttendanceResult{marks++;return decode(patch(fixture("registration").jsonObject,"status" to JsonPrimitive("attended")))} override suspend fun spaces(scope:MemberScope,page:Int):BookableSpaceListResponse=decode(buildJsonObject{put("count",1);put("results",JsonArray(listOf(fixture("space"))))}) override suspend fun bookings(scope:MemberScope,spaceId:Int,page:Int):BookingListResponse{bookingsReads++;return decode(buildJsonObject{put("count",1);put("results",JsonArray(listOf(fixture("booking"))))})} override suspend fun approveBooking(scope:MemberScope,id:Int):BookingAdmin{bookingFailure?.let{throw it};return decode(fixture("booking"))} diff --git a/app/src/test/resources/account/bootstrap.json b/app/src/test/resources/account/bootstrap.json index d826ba1..c1216aa 100644 --- a/app/src/test/resources/account/bootstrap.json +++ b/app/src/test/resources/account/bootstrap.json @@ -1,7 +1 @@ -{ - "branding": {}, "theme": {}, "edition": "community", "email_enabled": true, - "modules": ["membership", "member_accounts"], "features": ["membership.self_export"], "workflows": [], - "frontend": {"type":"web","hostname":"example.invalid","allowed_origins":[]}, - "public_api": {"base_url":"https://example.invalid/api/v1","inventory_path":"/inventory","publishable_key":"unused"}, - "makerspace": {"id":7,"name":"Workshop","slug":"workshop","public_code":"DEMO","location":"Kochi","timezone":"Asia/Kolkata","opening_hours":{},"map_url":"","logo_url":null,"cover_image_url":null,"public_stats_enabled":false,"public_site_enabled":true,"public_inventory_enabled":true,"public_landing_page":"auto","membership_policy":"request"} -} +{"branding":{},"theme":{},"edition":"community","email_enabled":true,"modules":["membership","member_accounts"],"features":["membership.self_export"],"workflows":[],"frontend":{"type":"web","hostname":"example.invalid","allowed_origins":[]},"public_api":{"base_url":"https://example.invalid/api/v1","inventory_path":"/inventory","publishable_key":"unused"},"makerspace":{"id":7,"name":"Workshop","slug":"workshop","public_code":"DEMO","location":"Kochi","timezone":"Asia/Kolkata","opening_hours":{},"map_url":"","logo_url":null,"cover_image_url":null,"public_stats_enabled":false,"public_site_enabled":true,"public_inventory_enabled":true,"public_landing_page":"auto","membership_policy":"request"},"messaging":{"sms":false,"whatsapp":false}} diff --git a/app/src/test/resources/staff/booking.json b/app/src/test/resources/staff/booking.json index af295a0..7c341a5 100644 --- a/app/src/test/resources/staff/booking.json +++ b/app/src/test/resources/staff/booking.json @@ -11,5 +11,9 @@ "public_token": "71000000-0000-4000-8000-000000000001", "space_id": 901, "starts_at": "2026-10-16T09:00:00Z", - "status": "pending" + "status": "pending", + "arrived_at": null, + "cancelled_at": null, + "restore_until": null, + "status_before_cancel": "" } diff --git a/app/src/test/resources/staff/event.json b/app/src/test/resources/staff/event.json index f8506a2..885fe83 100644 --- a/app/src/test/resources/staff/event.json +++ b/app/src/test/resources/staff/event.json @@ -39,5 +39,14 @@ "ticketing_features": [], "timezone_name": "", "title": "Sample repair workshop", - "updated_at": "2026-10-16T09:00:00Z" + "updated_at": "2026-10-16T09:00:00Z", + "audience_label": null, + "badge_sensitive_fields_allowed": false, + "broadcasts_enabled": false, + "can_manage_staff": false, + "organization_admission_blocked": false, + "participating_organizations": [], + "report_features": [], + "show_registration_count": false, + "staff_permissions": [] } diff --git a/app/src/test/resources/staff/registration.json b/app/src/test/resources/staff/registration.json index 33e8432..5538317 100644 --- a/app/src/test/resources/staff/registration.json +++ b/app/src/test/resources/staff/registration.json @@ -10,5 +10,9 @@ "phone": "", "quoted_amount": null, "status": "registered", - "ticket_type_id": null + "ticket_type_id": null, + "cancelled_at": null, + "no_longer_eligible": false, + "restore_until": null, + "status_before_cancel": "" } diff --git a/app/src/test/resources/staff/space.json b/app/src/test/resources/staff/space.json index 8f55f3e..6a72f9e 100644 --- a/app/src/test/resources/staff/space.json +++ b/app/src/test/resources/staff/space.json @@ -27,5 +27,18 @@ "requester_notifications_enabled": null, "show_public_availability": false, "show_public_booker_names": false, - "updated_at": "2026-10-16T09:00:00Z" + "updated_at": "2026-10-16T09:00:00Z", + "broadcasts_enabled": false, + "can_copy_booking_rules": false, + "can_manage_grants": false, + "checkin_enabled": false, + "clone_enabled": false, + "embed_enabled": false, + "passes_enabled": false, + "recurring_enabled": false, + "report_features": [], + "staff_grants_enabled": false, + "staff_permissions": [], + "staff_tools_enabled": false, + "website_enabled": false } diff --git a/branding/brand.json b/branding/brand.json index 01a7fd1..940c859 100644 --- a/branding/brand.json +++ b/branding/brand.json @@ -4,6 +4,7 @@ "ios_bundle_id": "org.spaceworks.app", "ios_team_id": "XXXXXXXXXX", "server_url": null, + "play_integrity_cloud_project_number": null, "support_url": null, "privacy_url": null, "theme_tokens": "design/tokens/tokens.json", diff --git a/docs/ACCOUNT.md b/docs/ACCOUNT.md index 40a8ff0..6696ca3 100644 --- a/docs/ACCOUNT.md +++ b/docs/ACCOUNT.md @@ -47,7 +47,12 @@ Privacy and support links come from validated optional `branding/brand.json` val configuration displays an honest unavailable state; no legal document or contact success is invented. Links normalize HTTPS/IDNA and open externally without credentials. -Whole-account deletion has no production route in the supplied contract. Debug alone offers +Whole-account deletion now uses the generated `GET/POST /auth/account/deletion` and +reauthentication operations (see [NATIVE-CONTRACT.md](NATIVE-CONTRACT.md)). **Delete account** +shows every server blocker with its server detail and an in-app fix link, the restore disclosure, +then reauthentication (password with authenticator code when enabled, phone OTP, or linked Google +with any provider second factor), the typed `DELETE` confirmation and a fresh blocker list on 409. +Success wipes the local session. Debug additionally keeps the isolated **Explore account deletion preview**: all planned blockers, actual demo makerspace IDs, supported fix destinations, fresh proof, exact typed DELETE, proof expiry, a newly discovered obligation and local preview reset. The preview uses `DemoPass!123` or code123456. It never deletes a real account, @@ -55,6 +60,7 @@ invokes an SDK or sends a deletion request. Staff fix navigation appears only fo selected staff preview; unsupported responsibilities point to makerspace support. The ordinary account security preview password is `sample`; manual password preview uses `DemoPass!123`. -Live server/provider/account-deletion verification remains deferred to the owner backend and the -native-contract requests in issue3. The English account catalog prepares localization; it does not +Live server/provider/account-deletion verification against an owner deployment remains a release +gate. Authenticator enrollment from a device grant, native provider linking and paged +session/export lists are still unavailable in the published backend. The English account catalog prepares localization; it does not claim translated languages or completed store submission. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 709cea5..b51af3f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,30 @@ # Phase notes +## V1 — v2 PRD extras: usage, staff dashboard, messaging consent, phone restore (2026-10-18) + +Built by Claude. Adds the backend v2 PRD features that were published but missing: member Home **Your activity** (my-usage), staff **Today at a glance** (dashboard counters, handover queue tiles, report widgets), Profile **Text and WhatsApp messages** consent, and **Restore a deleted account** by phone. All five operations go through the generated client and are listed in `scripts/contract.py`; copy lives in the new `insights_strings.xml` catalog (registered in `tokens/mobile-copy.json`). `NullEnum` is mapped to `JsonElement` to keep the generator compiling. + +Brand: new UI uses only semantic tokens and the existing Home components. Stat cards are neutral panels with Gilroy SemiBold tabular numbers in accent ink and full-opacity labels, on an equal-width grid whose column count follows width and font scale; bars use accent ink on the soft accent track (dark `accent` alone is under 3:1) and every value is also written as text; consent status is a word on a soft pill; no Unbounded on staff screens; 48dp rows. Reviewed on the Pixel_8a emulator in light and dark through the Debug demo; the review fixed wrapped stat cards, flat stat fills, low-contrast dark bars and unit-less values. + +Validation: unit and MockWebServer cases for my-usage (gated sections, series projection), dashboard (non-zero counters, handover queue), messaging read/write/refusal, restore (challenge reuse, attestation, server reason), state holders (scope reset, refetch on refusal, Retry-After); 578 Debug + 546 Release unit cases (1,124 executions) with zero failures/errors/skips, lint, both APKs (16 KB/ELF and feature-filter gates pass), bundle and androidTest compile; new `InsightsJourneyTest` UI journeys run on CI. + +Parity checklist: Home → Your activity (Loans: Borrowed/Returned/Out now + By month; Machine time: Hours, By month, Most used machines; Bookings + Events attended; Payments: Paid/Outstanding per currency; hidden sections when gated); Staff tools → Today at a glance (counters, Handover queue: Ready to issue/Due back/Pickups, Outstanding payments, Reports; "Nothing needs your attention right now."); Profile → Text and WhatsApp messages (SMS, WhatsApp; On/Off; "Messages go to …", verify-phone, unavailable, STOP-paused states); Sign-in → Restore a deleted account (phone → code + new password → Account restored). + +## C2–C7 — native backend contract wired end to end (2026-10-18) + +Built by Claude (no Codex in this effort). `api/openapi.json` is re-pinned to the mobile-backend snapshot `962e6a4b…` (760 paths); the generated client is regenerated and every previously "unavailable" native capability now uses generated operations. Details, owner inputs and remaining backend issues: [NATIVE-CONTRACT.md](NATIVE-CONTRACT.md). + +- **C2 contract re-pin** (`b123caa`): vendor-discriminated payment intents, attendance and booking unions, typed attestation payload, required `session_authority`, refreshed fixtures; four URI-or-empty inline unions mapped to `String`. +- **C3 native auth** (`863ad92`): Play Integrity Standard proofs (owner Cloud project number), `PublicConfig.native` method gating, non-dismissable update-required gate, member-only phone OTP device sign-in, `native_staff` staff gating, server-confirmed sign out everywhere. +- **C4 account deletion** (`71638f4`): blockers with fix links, restore disclosure, password/authenticator/phone/Google reauthentication with second factors, typed `DELETE`, 409/403 refreshed blockers, proof expiry and local wipe. +- **C5 push/links** (`afe658f`): v2 push links and App Links resolved by the server link contract; App Link filter restricted to member paths. +- **C6 OIDC/Razorpay** (`04d4e6e`): system-browser PKCE OIDC sign-in through the attested device route; Razorpay intent validation. +- **C7 release hardening** (`643a54d`): R8 minification and resource shrinking with library rules, owner-injected upload signing/version, release HTTPS-only system trust, mapping file in CI artifacts, new native-contract UI journeys. + +Validation: local `gradlew lint test assembleDebug assembleRelease bundleRelease assembleDebugAndroidTest` passes on Windows: 567 Debug + 535 Release unit cases (1,102 executions), zero failures/errors/skips; both APKs pass the 16 KB/ELF and feature-filter packaging gate; the R8 mapping keeps all 209 generated model serializers. CI [39129479460](https://github.com/SpaceWorks-HQ/SpaceWorks-Android/actions/runs/39129479460) passed build plus phone and tablet UI journeys at C3; later commits run on push. New MockWebServer cases cover phone start/confirm, OIDC nonce/route, logout-all, Play Integrity payload shape, deletion preview/reauth/phone/social/factor/delete (202/409/403/503) and Razorpay intents. C7 passed build plus phone and tablet UI journeys, including the new native-contract journeys, in PR run [39133467626](https://github.com/SpaceWorks-HQ/SpaceWorks-Android/actions/runs/39133467626). Local runtime smoke of the R8-minified release (debug-key signed copy, never distributed) on the Pixel_8a API 37 emulator: onboarding, skip, server entry and a real HTTPS generated health call rendered the mapped 404 copy with the brand font and logo and no app exception. Local instrumented tests cannot run on that API 37 preview image (the pinned Espresso calls the removed `InputManager.getInstance`); CI's supported images remain the UI gate. An owner signed-build smoke on a physical device is still required. + +Parity checklist (for comparison with iOS): sign-in shows only the published methods (password, phone, Google, OIDC; Apple iOS-only); update-required gate with store button and Try again; phone entry → code (resend countdown, change number, use another method, invalid/expired code); staff tools only for `native_staff`; Profile → Sign out everywhere confirmation; Account → Delete account: loading, blockers (server detail, fix links, web console for organization fixes), Verify it is you (password ± authenticator, phone code, Google, provider second factor), Confirm account deletion (typed `DELETE`), conflict, expired proof, deleted notice; payments open Stripe or Razorpay as the server selects; push/App Links open the contract destination or Inbox. Not built yet: my-usage, staff analytics, SMS/WhatsApp consent, account restore. + ## C1 — repository hardening and validated handoff (2026-10-18) Tested source [`3a400c7`](https://github.com/SpaceWorks-HQ/SpaceWorks-Android/commit/3a400c7bcf7c16164393ea493983a4bd173a710b) pins the existing CI actions to full upstream commits, disables checkout credential persistence and verifies the unchanged Gradle 8.13 distribution checksum. Existing read-only workflow permissions, fork-cache protections, native commands, profiles and timeouts remain intact. Narrow private-configuration ignores, contribution/security/dependency guidance and supplied-font provenance document the supported handoff; the exact existing Unbounded OFL is now packaged and verified in each APK. No SDK/toolchain version, app behavior or backend contract changes are included, and a fully locked, independently verified transitive dependency graph remains follow-up work. diff --git a/docs/GENERATED-CLIENT.md b/docs/GENERATED-CLIENT.md index e458d6b..1a54a8b 100644 --- a/docs/GENERATED-CLIENT.md +++ b/docs/GENERATED-CLIENT.md @@ -4,7 +4,7 @@ The pinned OpenAPI Generator 7.26.0 runs in an isolated process. `scripts/native_templates.py` reads its actual resolved jar, checks its Maven identity and exact upstream template hashes, and writes two build-only overrides. These preserve the original map-parent inheritance flags while emitting one constructor, annotate binary responses `@Streaming`, and apply `@JvmSuppressWildcards` to generated Retrofit interfaces. Seven Python checks run before builds; unknown versions/templates fail for review. Generated Kotlin and extracted templates stay out of Git. -Three bootstrap inline schema mappings use the generator's raw inline names: `TenantBootstrapMakerspace_map_url` is a native `String`, and `TenantBootstrapMakerspace_opening_hours` / `TenantBootstrapMakerspace_opening_hours_oneOf_weekly` are lossless `JsonElement`. The first wire value is a URI string or empty string; the latter are empty or structured JSON objects. These calendar values are retained and unused by capability projection. The mappings avoid unsupported inline union scalar serializers without changing the owner contract, authentication unions, or request/response wire fields. Capability tests decode real generated bootstrap responses and require the returned makerspace ID to match the requested scope. +Bootstrap and URI-or-empty inline schema mappings use the generator's raw inline names: `TenantBootstrapMakerspace_map_url`, `SocialLogin_redirect_uri`, `ReauthenticationSocialStart_redirect_uri`, `WebsiteSection_url` and `WebsiteSection_website_url` are native `String`s (each wire value is a URI string or an empty string), and `TenantBootstrapMakerspace_opening_hours` / `TenantBootstrapMakerspace_opening_hours_oneOf_weekly` are lossless `JsonElement`. The opening-hours values are empty or structured JSON objects. These calendar values are retained and unused by capability projection. `NullEnum` (an `enum: [null]` schema used only as the null arm of report-chart `partition`) is mapped to lossless `JsonElement`, because the generator emits an empty, uncompilable enum for it. The mappings avoid unsupported inline union scalar serializers without changing the owner contract, authentication unions, or request/response wire fields. Capability tests decode real generated bootstrap responses and require the returned makerspace ID to match the requested scope. Each authenticated generated call captures its makerspace header. The observing CallFactory seam retains the underlying OkHttp call only to cancel streamed bodies safely: cancellation invokes thread-safe `Call.cancel`, and the IO reader alone closes the response body. QR downloads validate SVG MIME and a 512 KiB limit; the native QR renderer rejects external resources, scripts, entities and unsupported SVG structures. JSON error parsing preserves code/detail precedence and readable nested field paths. diff --git a/docs/IMPLEMENTATION-PLAN.md b/docs/IMPLEMENTATION-PLAN.md index 90ca0b3..8a8524e 100644 --- a/docs/IMPLEMENTATION-PLAN.md +++ b/docs/IMPLEMENTATION-PLAN.md @@ -31,3 +31,5 @@ A5 is green ate2aaabb ([CI38967906796](https://github.com/SpaceWorks-HQ/SpaceWor A6 is green at9af9458 ([CI38971344990](https://github.com/SpaceWorks-HQ/SpaceWorks-Android/actions/runs/38971344990)): build and all36 native cases on each phone/tablet passed. Full-OS confirmation, partial assignment, kit and attendance views were reviewed. A7 now integrates account/security, global controls without membership, membership exit, generated exports and a Debug-only whole-account deletion preview. See `docs/ACCOUNT.md`; its complete local/native checks remain mandatory before the final A8 device matrix and release readiness pass. A7 is validated at0b0c9fc ([CI38981297516](https://github.com/SpaceWorks-HQ/SpaceWorks-Android/actions/runs/38981297516)): build and all50 native cases on each phone/tablet passed with zero skips. The final A8 checkpoint adds scoped first-use tips, the Profile app guide, actual module-aware booking navigation, saturated Retry-After handling, optional camera/device-filter verification and the measured ten-profile matrix. Every new component/full-app journey and all required matrix jobs must pass before accepting the release handoff. Actual Play signing/testing/upload and live backend/provider verification remain owner provisioning gates. + +Native contract checkpoint (2026-10-18, Claude): the owner's mobile backend published the 1a–1d contracts. `api/openapi.json` is re-pinned to `962e6a4b…` and the previously unavailable capabilities now use generated operations: Play Integrity attestation, native config/minimum version, phone OTP device sign-in, OIDC, `session_authority` staff gating, logout-all, whole-account deletion with reauthentication, vendor-selected Stripe/Razorpay intents and contract-based push/App Links. Release builds are R8-minified with owner-injected signing. See `docs/NATIVE-CONTRACT.md` for owner inputs, remaining backend issues and the v2-only features (my-usage, staff analytics, messaging consent, account restore) that are not built yet. diff --git a/docs/NATIVE-CONTRACT.md b/docs/NATIVE-CONTRACT.md new file mode 100644 index 0000000..3c04911 --- /dev/null +++ b/docs/NATIVE-CONTRACT.md @@ -0,0 +1,58 @@ +# Native backend contract (backend 1a–1d) + +`api/openapi.json` is pinned to the mobile-backend snapshot SHA-256 +`962e6a4b5b6927db0214c11e4b3b5ff688f660dccb8e674a7403b996cb1d8460` (760 paths). The backend's +`docs/mobile/MOBILE-APP-PRD.md` (v2) and `docs/mobile-integration.md` describe the rules the schema +cannot express. `docs/PRD.md` stays the byte-identical v1 owner copy; where the two differ, the +v2 text is read as the owner's update of the "planned (1a–1d)" items. Every call goes through the +generated client; `scripts/contract.py` lists each operation the app uses. + +## What the app now does against the real contract + +| Area | Behavior | Generated operations | +|---|---|---| +| Attestation | Debug emulator → `{"type":"development"}` (open-object arm). Every other build → Play Integrity Standard, `requestHash = base64url(SHA-256(challenge))`, sent as `{"type":"play_integrity","token":…}`. Without a configured Cloud project number, device sign-in is visibly unavailable. | attestation-challenge | +| Sign-in methods | Rendered from `PublicConfig.native.providers` when present (password, phone, Google, OIDC); legacy flags otherwise. Apple is iOS-only (its native audience binds to the iOS app ID). | config | +| Minimum version | Below `native.min_app_version.android` (dotted numeric compare) → non-dismissable "Update required" with Google Play and Try again. A stored session is kept and resumes after the update. | config | +| Password + MFA | Unchanged device login; enrollment-at-login, verify and recovery codes. | device login, mfa enroll/confirm/verify | +| Phone OTP | Member-only. Start with `delivery=device`, the attestation challenge and `client_platform=android`; confirm with the same challenge, a fresh proof and the six-digit code. Handles device tokens, MFA challenge and rejects completions without a device grant. 30 s local resend, server `Retry-After` honored. | phone login start/confirm | +| Google | Credential Manager with the server nonce; `serverClientId` = `native.providers.google.android_client_id`. | social nonce, social google | +| OIDC | Published `oidc:{slug}` providers: system browser, authorization code + PKCE S256 against the issuer's discovery document, redirect `{applicationId}:/oidc`, ID token sent to the attested device route. | social nonce, social oidc | +| Staff authority | Staff tools only when `session_authority == "native_staff"` **and** the selected membership has the action; `X-Makerspace-Id` is always the selected membership. | auth me | +| Sign out everywhere | Push cleanup, then `POST /auth/logout-all`; local session is cleared only after the server confirms. | logout-all | +| Account deletion | Preview with every blocker (server detail kept, unknown codes shown as-is, in-app fix links, organization fixes only to the deployment's own host), restore disclosure, reauthentication (password ± authenticator, phone OTP, Google with provider second factor), typed `DELETE`, fresh 409/403 blockers, `recent_reauth_required` → verify again, `deletion_retry` → refetch, proof expiry. Success wipes the local session. | account deletion GET/POST, reauth GET/POST, reauth phone start/confirm, reauth social start/complete/factor | +| Payments | `vendor`-discriminated intents: Stripe PaymentSheet or Razorpay Checkout (`amount_minor` passed unchanged). Each is validated before presentation; settlement only from the refetched payment list. | payments list, mobile-intent, documents | +| Push v2 + links | v2 `link` and verified App Links resolve the server link contract (`/m/{slug}/member[/tab]`, event tickets, events, bookings, items); custom-domain push forms use the hint's makerspace; unsupported links open Inbox. The App Link filter claims only those member paths. | push devices create/delete, inbox, preferences | +| Member usage | Home "Your activity": loans (borrowed/returned/out now + monthly), machine hours and top machines, bookings, events attended, paid/outstanding per currency. Absent sections stay hidden; series use the server's declared x field and first measure, with every value written out. Cleared on sign-out and makerspace switch. | my-usage | +| Staff dashboard | "Today at a glance" above the staff request tabs: server counters, handover `queue_tiles` (handover scope shows no analytics), outstanding per currency and report widgets with a single headline number. | admin dashboard | +| SMS/WhatsApp consent | Profile → "Text and WhatsApp messages", shown when the bootstrap's `messaging` reports a channel. One switch per channel with an On/Off word; opt-in needs a verified phone and an available, unsuppressed channel; opt-out is always allowed; refusals (409/423/503) show the server reason and refetch. | me/messaging GET, consent POST | +| Account restore | Sign-in → "Restore a deleted account" (when phone sign-in is published): phone login start with a device challenge, then code + new password through `restore/phone` with attestation. Never returns a session; the person signs in afresh. | phone login start, account restore phone | + +## Owner inputs still required (not in the repository) + +| Input | Where it goes | +|---|---| +| Play Integrity Cloud project **number** | `branding/brand.json` → `play_integrity_cloud_project_number`, or `-Pspaceworks.playIntegrityProject=` in the release environment. The server needs the Play Integrity service account and the app registration with the Play App Signing SHA-256 signers. | +| Firebase `google-services.json` | Ignored `app/google-services.json` for the exact application ID (the Google Services plugin applies only when present). Server needs the FCM service account. | +| Google OAuth audience | The value the server publishes as **"Google android client id"** is what Credential Manager sends as `serverClientId`; Google requires a **Web application** OAuth client there. Also register an Android OAuth client for the package + signing SHA-1 in the same Cloud project. | +| OIDC redirect URI | Register `{applicationId}:/oidc` (e.g. `org.spaceworks.app:/oidc`) as an allowed redirect for each enabled public OIDC client with PKCE. | +| Release signing | `SPACEWORKS_UPLOAD_KEYSTORE`, `SPACEWORKS_UPLOAD_KEYSTORE_PASSWORD`, `SPACEWORKS_UPLOAD_KEY_ALIAS`, `SPACEWORKS_UPLOAD_KEY_PASSWORD` environment variables; `-Pspaceworks.versionCode=` / `-Pspaceworks.versionName=` per upload. Keep `app/build/outputs/mapping/release/mapping.txt` with each upload. | +| App Links | A fixed HTTPS `server_url` in `brand.json`, and the server's `/.well-known/assetlinks.json` publishing the production signing SHA-256. | +| Payment providers | Stripe/Razorpay credentials per makerspace on the server; the app receives only runtime public values. | + +## Backend contract issues found (documented, not worked around) + +1. `DeviceAttestationPayload.type` is generated as a single-value enum (`play_integrity`); App Attest and development proofs must use the open-object arm. The v2 PRD acknowledges this. +2. `SocialLoginResponse` has no MFA-challenge arm although the v2 PRD says social sign-in can return one. Backend code marks provider sign-in as MFA-satisfied and always returns device tokens; the app treats social as tokens-only. +3. `POST /auth/mfa` still refuses device grants ("Enroll through browser sign-in"), so optional authenticator enrollment stays unavailable in the app (login-time enrollment works). +4. `POST /auth/social/providers` still accepts only `client_platform: web`; native provider linking stays unavailable. +5. Account sessions and exports list operations still declare no `page` query parameter, although responses are paginated. +6. `mobile-integration.md` calls the Google values "native OAuth client IDs"; on Android the published value must be the Web client used as `serverClientId` (see owner inputs). +7. There is no published native OIDC redirect-URI convention; the app uses `{applicationId}:/oidc`. +8. Every `Dashboard` counter declares `default: 0`, so the generated client cannot distinguish an omitted (unavailable) counter from zero, contrary to the v2 PRD. The app shows only non-zero counters. +9. `NullEnum` (`enum: [null]`, the null arm of report-chart `partition`) makes OpenAPI Generator 7.26 emit an empty enum; it is mapped to `JsonElement`. +10. Several optional URI fields are `oneOf[uri, maxLength 0]` inline unions that OpenAPI Generator 7.26 cannot serialize; they are mapped to `String` without changing the wire contract (see `GENERATED-CLIENT.md`). + +## Not yet built + +Email and social **account restore** stay on the web (the email landing page is browser-only; native social restore needs a fresh provider proof flow not yet built). The staff analytics report browser (`reports/catalog`, `analytics/{key}`) and the new ticket/calendar download routes are not implemented. Live integration against a deployed backend and real-device provider round trips (Play Integrity, FCM, Google/OIDC, Stripe/Razorpay, SMS) remain unverified. diff --git a/docs/PAYMENTS.md b/docs/PAYMENTS.md index dac3e16..f72343a 100644 --- a/docs/PAYMENTS.md +++ b/docs/PAYMENTS.md @@ -2,7 +2,7 @@ Home dues and Profile open the selected makerspace's payments. Amounts, currencies, due states, settlement metadata and refunds come from the generated payment-list operation. Unknown statuses cannot enable checkout. A payment link requires a successful fresh list read containing its actual ID; a refused or superseded response cannot open a cached charge. -The current owner contract returns Stripe mobile-intent configuration. The Activity registers Stripe PaymentSheet and the approved Razorpay adapter before STARTED, but only the documented server-selected Stripe intent is launchable in production. No Razorpay API shape, order or rail selector is invented. A later server contract can supply its adapter inputs. Owner provider keys, merchant setup and live native sessions remain required. +The mobile intent is vendor-discriminated by the server. `stripe` opens PaymentSheet with the returned client secret and publishable key; `razorpay` opens Checkout with `key_id`, `order_id`, the server's integer `amount_minor` (passed unchanged), currency, name and description. Each intent is validated before presentation; the app never chooses the rail. Owner provider keys and merchant setup remain required. Checkout rechecks the charge before requesting an intent. SDK credentials remain ephemeral and redacted; no amount is calculated or changed locally. Every SDK closure, including cancellation, failure and interruption, requires an authoritative payment-list refresh. A submitted SDK result is never displayed as paid. Bounded polling can reach `paid_online`, `paid_offline` or another actual status; an unresolved result stays pending with an explicit status check. An offline or throttled callback remains blocked until its refresh succeeds. Restored opaque callback references cannot identify a charge or transfer an old outcome to another account. diff --git a/docs/PUSH-AND-LINKS.md b/docs/PUSH-AND-LINKS.md index 2a4eb28..67636ec 100644 --- a/docs/PUSH-AND-LINKS.md +++ b/docs/PUSH-AND-LINKS.md @@ -22,7 +22,7 @@ Entering demo pauses the live delivery gate synchronously. Late token callbacks Only selected-origin HTTPS, the DEBUG validated loopback origin, and the custom scheme are accepted. Credentials, external origins, queries/fragments, encoded/control/format characters, backslashes and traversal are refused. Allowed product paths are `/makerspaces/{positiveID}/` followed by `home`, `inventory[/positiveProductID]`, `activity`, `inbox`, `profile`, `payments[/positivePaymentID]`, `bookings[/UUID]`, `events[/UUID]`, `spaces[/UUID]`, or `registrations/{positiveRegistrationID}`. Unknown maker routes fall back to authorized Inbox. These are mobile product routes, not new backend operations. -The exact existing web aliases `/m/{slug}/member` and `/m/{slug}/admin/notifications` resolve only against freshly authorized memberships. The member alias opens Home; the staff alias additionally needs native staff authority, currently unavailable in the pinned contract. A hint's maker cannot be overridden by its link. Missing `v` means1; only v2 may supply the extended event/inbox/link fields. Future or malformed versions fall back to an authorized maker Inbox. Unknown keys never grant authority. +Server links follow the backend's mobile link contract (`mobile_link_contract.py`): `/m/{slug}/member` (Home), `/m/{slug}/member/{tab}` (overview, loans, bookings, events, payments, inbox, account/membership; unknown tabs open Home), `/m/{slug}/member/events/{registration}/ticket`, `/m/{slug}/events/{token}`, `/m/{slug}/bookings/{token}` and `/m/{slug}/items/{item}`, with an optional trailing slash. Slugs resolve only against freshly authorized memberships. A v2 push link in the custom-domain form (no `/m/{slug}`) is resolved under the hint's own makerspace. `/m/{slug}/admin/notifications` additionally needs `native_staff` authority. The verified HTTPS intent filter claims only the member path patterns above; admin, control and API paths stay in the browser. Notifications with the same `event_id` replace each other. A hint's maker cannot be overridden by its link. Missing `v` means1; only v2 may supply the extended event/inbox/link fields. Future or malformed versions fall back to an authorized maker Inbox. Unknown keys never grant authority. Every tapped hint freshly validates identity, selects the authorized scope, successfully refetches Inbox, then refetches its destination. A refused Inbox blocks target navigation. Booking/registration targets use actual returned scoped IDs; missing targets return to Activity. Current forms are retained with an explicit pending-update action. Links never automatically request, pay, book, issue, return or check in. A foreground receipt refreshes only the already selected maker's Inbox after fresh identity; it never switches the visible maker. diff --git a/docs/RELEASE-READINESS.md b/docs/RELEASE-READINESS.md index 0f3ae6e..52081a0 100644 --- a/docs/RELEASE-READINESS.md +++ b/docs/RELEASE-READINESS.md @@ -8,8 +8,8 @@ checks and artifact/visual review, then a guarded merge of only the reviewed hea ## Reproduce the app Clone recursively and retain the reviewed `design/` commit. Install JDK 21, Python 3 and Android -SDK platform 36/build tools. `api/openapi.json` is the authoritative contract; do not replace it -with another checkout's schema. From the repository root: +SDK platform 36/build tools. `api/openapi.json` is the authoritative contract (re-pinned to the mobile-backend +snapshot `962e6a4b…`; see `docs/NATIVE-CONTRACT.md`); do not replace it without an owner update. From the repository root: ```sh ./gradlew lint test assembleDebug assembleRelease bundleRelease @@ -18,9 +18,11 @@ with another checkout's schema. From the repository root: On Windows use `gradlew.bat`. SDK location belongs in the ignored `local.properties` or the build environment. Gradle generates semantic colors, branding assets/motion values and the API client; do not edit generated output. The debug APK is `app/build/outputs/apk/debug/app-debug.apk`. -The unsigned Release APK is under `app/build/outputs/apk/release/` and the unsigned bundle under -`app/build/outputs/bundle/release/`; these artifacts prove compilation/packaging, not Play -distribution readiness. +The Release build is R8-minified and resource-shrunk; its APK is under +`app/build/outputs/apk/release/`, the bundle under `app/build/outputs/bundle/release/` and the +mapping file under `app/build/outputs/mapping/release/` (also a CI artifact). Without the owner's +`SPACEWORKS_UPLOAD_*` signing environment these are unsigned and prove compilation/packaging, not +Play distribution readiness. Minified runtime behavior must be smoke-tested on a signed owner build. ## Repository and supply-chain handoff @@ -120,9 +122,11 @@ the generated application ID controls deployment identity. | Camera/evidence | Camera remains optional hardware. Test first-use explanation, permission request/denial, manual entry, rotation, cancellation and explicitly confirmed photo upload. No background camera or scan-triggered mutation. | | Payments | Server chooses vendor and supplies runtime public checkout input. Provider secrets stay on the server. Test configured sandbox success/cancel/failure, delayed webhook, process recreation and later status refresh; only the server can confirm paid status. | -The [backend dependency issue](https://github.com/SpaceWorks-HQ/SpaceWorks-Android/issues/3) -tracks native configuration/attestation, staff authority, push v2/server link files, Razorpay -checkout fields and whole-account deletion. The owner deferred those services. Complete labelled +The mobile backend now publishes native configuration/attestation, phone device delivery, staff +authority, push v2/link contract, Razorpay intents, logout-all and whole-account deletion; the app +uses them through generated operations ([NATIVE-CONTRACT.md](NATIVE-CONTRACT.md) lists owner +inputs and the remaining backend issues). They are verified with MockWebServer/unit and emulator +UI tests only; live deployment round trips remain owner gates. Complete labelled debug demo journeys are UI fixtures; they do not make a live provider configured or perform a charge/deletion. A release must use supported generated operations and genuine authorization. Account management has additional confirmed native limitations: current backend code refuses @@ -191,7 +195,7 @@ Record distribution evidence after owner provisioning; do not put credentials in | Full device-matrix CI run | The final handoff must link successful exact-source dev-push and ten-profile runs, observed profile evidence and actually reviewed full-OS images; fresh brand/motion validation remains required | | Signed bundle / versionCode / certificate fingerprint | Owner signing environment required | | Real-device provider test record | Owner backend/configuration required | -| Whole-account deletion and web resource verification | Updated owner contract/site required | +| Whole-account deletion and web resource verification | Implemented against the generated contract; owner deployment round trip and public web deletion resource required | | Play test-track build / tester acceptance | Owner Play Console access required | [PR #2](https://github.com/SpaceWorks-HQ/SpaceWorks-Android/pull/2) records the historical A8 checkpoint. It does not substitute for final proof of later brand, SDK appearance, motion or accessibility corrections. diff --git a/docs/STAFF.md b/docs/STAFF.md index d5d18ca..87180c9 100644 --- a/docs/STAFF.md +++ b/docs/STAFF.md @@ -4,11 +4,11 @@ The native staff interface covers pending request quantities and accept/reject r issue and return, direct handout, booking approval/refusal and event attendance confirmation. Scanning identifies equipment or a person; it never performs a physical action by itself. -The current published native session contract does not establish `native_staff` authority. -Production staff navigation and generated operations therefore remain closed. A membership role or -action list alone cannot grant access. The owner-deferred backend requirement is tracked in -[issue3](https://github.com/SpaceWorks-HQ/SpaceWorks-Android/issues/3). Live inventory, evidence -upload and attendance success are not claimed. +Staff tools open only when `/auth/me` reports `session_authority: "native_staff"` **and** the +selected membership grants the action. Password and social device sign-in snapshot staff +authority; phone sign-in never does, and a member promoted after sign-in must sign in again. Every +staff call carries the selected makerspace in `X-Makerspace-Id`. Live inventory, evidence upload +and attendance success against an owner deployment are not claimed. ## Explore the flow diff --git a/docs/WHITE-LABEL.md b/docs/WHITE-LABEL.md index ccdb01d..61c42bc 100644 --- a/docs/WHITE-LABEL.md +++ b/docs/WHITE-LABEL.md @@ -12,7 +12,7 @@ No secrets, signing keys, `.p8` files, Firebase config or test credentials are c Auth/intro branding also reads `logo_symbol`, `logo_symbol_reversed`, `logo_symbol_one_colour_ink` and the equivalent `logo_stacked` paths from `brand.json`. Supply native-vector-compatible SVG masters with their original view boxes; keep the dedicated symbol/stacked clear space and do not animate/recolor/tint the logo. Mobile motion comes from this app repository's `tokens/mobile-motion.json` and UX guidance from `docs/mobile-auth-ux-spec.md`; system animation-off settings suppress custom movement/opacity transitions. The existing Design submodule supplies brand assets and semantic colors; mobile-specific files are maintained in the app repositories. The default open-source Unbounded accent font and its conversion provenance/licence are in `docs/licenses/`; normal text uses the supplied Gilroy family. The `font_regular`, `font_medium`, `font_semibold` and `font_bold` paths select the four WOFF1 inputs; `scripts/brand_fonts.py` losslessly repackages their sfnt tables into native Android TTF resources during `prepareBrand`. See [font provenance](licenses/Gilroy-native-fonts.md). -Native Google acquisition uses Credential Manager 1.6.0 and the compiler-compatible GoogleID 1.1.1. Register your own package/signing fingerprints and server OAuth web client audience, and publish the documented `social_auth.google` configuration. Do not substitute an Android OAuth client ID for the server web audience. Missing native attestation/session contract still prevents production sign-in. The small Google G is Google's unchanged official sign-in asset ([source](https://developers.google.com/static/identity/images/g-logo.png), [branding requirements](https://developers.google.com/identity/branding-guidelines)); provider styling is intentionally independent of the app palette. No client secret is bundled. DEBUG preview providers never call Google/Apple/SSO/SMS SDKs or your backend; release omits their implementation and Explore demo entry. +Native Google acquisition uses Credential Manager 1.6.0 and the compiler-compatible GoogleID 1.1.1. The app passes `/config` `native.providers.google.android_client_id` as Credential Manager's `serverClientId`, so the server's "Google android client id" field must hold a **Web application** OAuth client ID; register an Android OAuth client for your package and signing SHA-1 in the same Google Cloud project. Production device sign-in also needs your Play Integrity Cloud project number (`play_integrity_cloud_project_number` in `brand.json`), and each OIDC provider must allow the redirect `{applicationId}:/oidc`. Release signing reads `SPACEWORKS_UPLOAD_KEYSTORE`, `SPACEWORKS_UPLOAD_KEYSTORE_PASSWORD`, `SPACEWORKS_UPLOAD_KEY_ALIAS` and `SPACEWORKS_UPLOAD_KEY_PASSWORD` from the private environment. See [NATIVE-CONTRACT.md](NATIVE-CONTRACT.md). The small Google G is Google's unchanged official sign-in asset ([source](https://developers.google.com/static/identity/images/g-logo.png), [branding requirements](https://developers.google.com/identity/branding-guidelines)); provider styling is intentionally independent of the app palette. No client secret is bundled. DEBUG preview providers never call Google/Apple/SSO/SMS SDKs or your backend; release omits their implementation and Explore demo entry. Payment SDK styling uses their documented native appearance interfaces. Stripe receives both semantic brand palettes, 10 dp control corners, standard SDK text sizing, and a native Gilroy XML family with explicit 400/500/600/700 weights. Stripe retains its native layout, accessible controls and system light/dark selection. Razorpay Android Standard Checkout receives only the documented `theme.color` accent; merchant identity/logo remain deployment/provider supplied. Its current Android API does not document a custom font or complete dark palette. The app does not inject unsupported options or restyle provider verification screens. These appearance settings do not confirm payment: existing server refetch and callback ownership checks remain authoritative. diff --git a/docs/mobile-auth-ux-spec.md b/docs/mobile-auth-ux-spec.md index c42a968..6e7e5fc 100644 --- a/docs/mobile-auth-ux-spec.md +++ b/docs/mobile-auth-ux-spec.md @@ -8,7 +8,7 @@ The owner requested a complete, polished onboarding/authentication experience in The original mobile PRD remains the scope owner: `SpaceWorks/docs/mobile/MOBILE-APP-PRD.md`. The owner has deferred backend work. Build complete, explicitly labelled debug/demo journeys for unavailable contracts; production must continue to use generated operations and fail safely where native authorization is unavailable. Do not create new routes, payload fields, access grants, successful payments, or deletion results to make screens appear complete. -Contract reference: the apps' `api/openapi.json`, SHA-256 `d5ec904eb2a52ca948ee38dae7840c8859a43e034a22f3b8dc478fd8851cbb9b`. Paths below include `/api/v1`, as do the generated operations. Configure the server origin without duplicating that prefix. A different schema in a local backend worktree is not a replacement contract. +Contract reference: the apps' `api/openapi.json`, SHA-256 `962e6a4b5b6927db0214c11e4b3b5ff688f660dccb8e674a7403b996cb1d8460` (re-pinned 2026-10-18 to the mobile-backend snapshot; see `docs/NATIVE-CONTRACT.md`). Paths below include `/api/v1`, as do the generated operations. Configure the server origin without duplicating that prefix. A different schema in a local backend worktree is not a replacement contract. Brand inputs: `design/tokens/tokens.json` is authoritative for color; `tokens/mobile-motion.json` is the shared machine-readable mobile motion contract generated into each app; `design/docs/ui-brand-spec.md` supplies the logo, spacing, motion provenance and voice; `design/docs/ux-yuyu-spec.md` and `design/docs/ux-screens-longtail.md` supply task-based layouts and states; `design/brand/space-works-brand-guidelines-v1.pdf` and its supplied page exports support visual verification. The owner subsequently requested the supplied Gilroy Regular/Medium/SemiBold/Bold faces for native content, with explicit native weight mapping and full system text scaling; Unbounded remains a short accent only. Preserve each font’s provenance. The owner’s asset-use instruction does not itself establish app-embedding/distribution rights: that separate legal release input must be confirmed, and the old free Light/ExtraBold EULA does not license these four supplied faces. diff --git a/scripts/contract.py b/scripts/contract.py index 3869877..3d640a0 100644 --- a/scripts/contract.py +++ b/scripts/contract.py @@ -66,6 +66,22 @@ 'api_v1_admin_bookings_reject_create', } phase3_operations |= {'api_v1_member_makerspaces_account_exports_download_create', 'api_v1_auth_device_grants_destroy', 'api_v1_member_makerspaces_account_sessions_destroy', 'api_v1_auth_mfa_recovery_create', 'api_v1_member_makerspaces_account_exports_create', 'api_v1_member_makerspaces_account_exports_retrieve', 'api_v1_auth_phone_link_start_create', 'api_v1_auth_device_grants_list', 'api_v1_auth_phone_destroy', 'api_v1_auth_mfa_retrieve', 'api_v1_member_makerspaces_account_leave_create', 'api_v1_member_makerspaces_account_exports_retrieve_2', 'api_v1_auth_mfa_disable_create', 'api_v1_auth_social_providers_destroy', 'api_v1_auth_phone_link_confirm_create', 'api_v1_member_makerspaces_account_cancel_create', 'api_v1_data_exports_download_retrieve', 'api_v1_member_makerspaces_account_membership_retrieve', 'api_v1_auth_social_providers_list', 'api_v1_member_makerspaces_account_sessions_retrieve'} +# Backend 1a-1d native contracts: phone device sign-in, OIDC, logout-all, deletion and reauthentication. +phase3_operations |= { + 'api_v1_auth_phone_login_start_create','api_v1_auth_phone_login_confirm_create', + 'api_v1_auth_social_oidc_create','api_v1_auth_logout_all_create', + 'api_v1_auth_account_deletion_retrieve','api_v1_auth_account_deletion_create', + 'api_v1_auth_reauth_retrieve','api_v1_auth_reauth_create', + 'api_v1_auth_reauth_phone_start_create','api_v1_auth_reauth_phone_confirm_create', + 'api_v1_auth_reauth_social_start_create','api_v1_auth_reauth_social_complete_create', + 'api_v1_auth_reauth_social_factor_create', +} +# Backend v2 additions: member usage, staff dashboard, messaging consent and phone account restore. +phase3_operations |= { + 'api_v1_member_makerspaces_my_usage_retrieve','api_v1_admin_makerspace_dashboard_retrieve', + 'api_v1_me_messaging_retrieve','api_v1_me_messaging_consent_create', + 'api_v1_auth_account_restore_phone_create', +} base_paths=set(selected) base_operations={op['operationId'] for path in base_paths for op in s['paths'][path].values() if isinstance(op,dict) and 'operationId' in op} found=set() diff --git a/tokens/mobile-copy.json b/tokens/mobile-copy.json index 5e656b5..ac6f2c1 100644 --- a/tokens/mobile-copy.json +++ b/tokens/mobile-copy.json @@ -40,5 +40,11 @@ "package": "org.spaceworks.app.guidance", "object": "UsageStrings", "output": "app/src/main/java/org/spaceworks/app/guidance/UsageStrings.kt" + }, + { + "catalog": "app/src/main/res/values/insights_strings.xml", + "package": "org.spaceworks.app.insights", + "object": "InsightsStrings", + "output": "app/src/main/java/org/spaceworks/app/insights/InsightsStrings.kt" } ]