{"swagger":"2.0","info":{"title":"HobbiHopp Developer API","description":"Manage your HobbiHopp classes and view bookings. Authenticate with an API key created on the HobbiHopp host website, sent as: Authorization: Bearer hh_live_...","version":"1.0.0"},"host":"hobbihopp-api.web.app","basePath":"/","schemes":["https"],"consumes":["application/json"],"produces":["application/json"],"securityDefinitions":{"api_key":{"type":"apiKey","name":"Authorization","in":"header","description":"Bearer hh_live_... (include the word 'Bearer' and a space before your key)"}},"security":[{"api_key":[]}],"paths":{"/v1/me":{"get":{"operationId":"GetMe","summary":"Test connection","description":"Returns the host and scopes for the supplied API key.","responses":{"200":{"description":"Key info","schema":{"$ref":"#/definitions/MeResponse"}},"401":{"description":"Invalid or revoked API key","schema":{"$ref":"#/definitions/Error"}}}}},"/v1/classes":{"get":{"operationId":"ListClasses","summary":"List classes","parameters":[{"name":"status","in":"query","type":"string","enum":["active","past"],"description":"Filter by class status"},{"name":"from","in":"query","type":"string","format":"date-time","description":"Only classes on/after this date-time"},{"name":"to","in":"query","type":"string","format":"date-time","description":"Only classes on/before this date-time"},{"name":"limit","in":"query","type":"integer","maximum":50,"default":20},{"name":"startAfter","in":"query","type":"string","description":"Cursor: the id of the last class from the previous page"}],"responses":{"200":{"description":"Classes","schema":{"$ref":"#/definitions/ClassListResponse"}}}},"post":{"operationId":"CreateClass","summary":"Create a class","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/ClassCreate"}}],"responses":{"201":{"description":"Created class","schema":{"$ref":"#/definitions/ClassResponse"}},"422":{"description":"Validation failed","schema":{"$ref":"#/definitions/Error"}}}}},"/v1/classes/{classId}":{"get":{"operationId":"GetClass","summary":"Get a class","parameters":[{"name":"classId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Class","schema":{"$ref":"#/definitions/ClassResponse"}},"404":{"description":"Not found","schema":{"$ref":"#/definitions/Error"}}}},"patch":{"operationId":"UpdateClass","summary":"Update a class","description":"Partial update. maxStudents cannot be reduced below the current number of enrolled students.","parameters":[{"name":"classId","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/ClassUpdate"}}],"responses":{"200":{"description":"Updated class","schema":{"$ref":"#/definitions/ClassResponse"}},"404":{"description":"Not found","schema":{"$ref":"#/definitions/Error"}},"422":{"description":"Validation failed","schema":{"$ref":"#/definitions/Error"}}}},"delete":{"operationId":"CancelClass","summary":"Cancel a class","description":"Soft-cancels (deactivates) a class with no enrolled students. Classes with enrolled students must be cancelled from the HobbiHopp app so refunds are handled.","parameters":[{"name":"classId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Cancelled class","schema":{"$ref":"#/definitions/ClassResponse"}},"404":{"description":"Not found","schema":{"$ref":"#/definitions/Error"}},"409":{"description":"Class has enrolled students","schema":{"$ref":"#/definitions/Error"}}}}},"/v1/classes/{classId}/bookings":{"get":{"operationId":"ListClassBookings","summary":"List bookings for a class","parameters":[{"name":"classId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Bookings","schema":{"$ref":"#/definitions/BookingListResponse"}},"404":{"description":"Not found","schema":{"$ref":"#/definitions/Error"}}}}},"/v1/classes/{classId}/message":{"post":{"operationId":"MessageClassStudents","summary":"Message all students in a class","description":"Sends an in-app message (with push notification) to every student with a confirmed booking in the class.","parameters":[{"name":"classId","in":"path","required":true,"type":"string"},{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/MessageSend"}}],"responses":{"201":{"description":"Messages sent","schema":{"$ref":"#/definitions/BroadcastResponse"}},"404":{"description":"Not found","schema":{"$ref":"#/definitions/Error"}},"409":{"description":"No confirmed students","schema":{"$ref":"#/definitions/Error"}}}}},"/v1/messages":{"post":{"operationId":"MessageStudent","summary":"Message a student","description":"Sends an in-app message to a student who has a booking with you.","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/DirectMessageSend"}}],"responses":{"201":{"description":"Message sent","schema":{"$ref":"#/definitions/MessageResponse"}},"404":{"description":"Student has no booking with you","schema":{"$ref":"#/definitions/Error"}},"422":{"description":"Validation failed","schema":{"$ref":"#/definitions/Error"}}}}},"/v1/reports/summary":{"get":{"operationId":"GetSummary","summary":"Overview at a glance","description":"Headline numbers in one call: classes, students, bookings, attendance, rating, and revenue (earned / available / pending).","responses":{"200":{"description":"Summary","schema":{"$ref":"#/definitions/SummaryResponse"},"examples":{"application/json":{"data":{"classes":{"total":24,"active":5,"past":19},"students":{"total":138,"active":42},"bookings":{"total":310,"confirmed":47,"completed":250,"cancelled":13},"attendance":{"attended":236,"rate":94.4},"rating":{"average":4.8,"reviewCount":96},"revenue":{"earned":11250.5,"available":8400,"pending":2850.5,"currency":"USD"}}}}}}}},"/v1/reports/revenue":{"get":{"operationId":"GetRevenue","summary":"Your earnings","description":"Detailed earnings — the same figures shown on the app's Earnings screen. Total earned, available to withdraw, pending (in holding period), total paid out, and a month-by-month breakdown.","responses":{"200":{"description":"Revenue","schema":{"$ref":"#/definitions/RevenueResponse"},"examples":{"application/json":{"data":{"totalEarnings":11250.5,"availableBalance":8400,"pendingBalance":2850.5,"totalPaidOut":6000,"totalBookings":250,"totalClassesTaught":19,"lastPayoutDate":"2026-06-20T00:00:00.000Z","earningsByMonth":{"2026-05":3200,"2026-06":4100.5},"lastUpdated":"2026-07-03T08:00:00.000Z","currency":"USD"}}}}}}},"/v1/reports/payouts":{"get":{"operationId":"ListPayouts","summary":"Your payouts","description":"History of withdrawals with status and dates.","parameters":[{"name":"status","in":"query","type":"string","enum":["pending","processing","completed","failed","cancelled"]},{"name":"limit","in":"query","type":"integer","maximum":50,"default":20},{"name":"startAfter","in":"query","type":"string"}],"responses":{"200":{"description":"Payouts","schema":{"$ref":"#/definitions/PayoutListResponse"},"examples":{"application/json":{"data":[{"id":"po_1","amount":6000,"currency":"USD","status":"completed","payoutMethod":"bank_transfer","bankAccountLast4":"4242","requestedAt":"2026-06-18T00:00:00.000Z","processedAt":"2026-06-19T00:00:00.000Z","completedAt":"2026-06-20T00:00:00.000Z"}],"nextCursor":null}}}}}},"/v1/reports/classes":{"get":{"operationId":"GetClassReport","summary":"Per-class breakdown","description":"A row per class with fill rate, bookings, attendance, revenue, and rating.","responses":{"200":{"description":"Class report","schema":{"$ref":"#/definitions/ClassReportResponse"},"examples":{"application/json":{"data":[{"classId":"cls_pottery","title":"Pottery for Beginners","dateTime":"2026-09-15T18:00:00.000Z","isActive":true,"maxStudents":10,"enrolledStudents":8,"fillRate":80,"bookings":{"confirmed":8,"cancelled":1},"attendance":{"attended":7,"rate":87.5},"revenue":360,"rating":4.8,"reviewCount":12}]}}}}}},"/v1/webhooks":{"get":{"operationId":"ListWebhooks","summary":"List webhooks","responses":{"200":{"description":"Webhooks","schema":{"$ref":"#/definitions/WebhookListResponse"}}}},"post":{"operationId":"CreateWebhook","summary":"Create a webhook","description":"Subscribes an https URL to events. The response includes the signing secret exactly once — deliveries carry an X-HobbiHopp-Signature header (sha256 HMAC of the body).","parameters":[{"name":"body","in":"body","required":true,"schema":{"$ref":"#/definitions/WebhookCreate"}}],"responses":{"201":{"description":"Webhook created (includes secret)","schema":{"$ref":"#/definitions/WebhookResponse"}},"409":{"description":"Webhook limit reached","schema":{"$ref":"#/definitions/Error"}},"422":{"description":"Validation failed","schema":{"$ref":"#/definitions/Error"}}}}},"/v1/webhooks/{webhookId}":{"delete":{"operationId":"DeleteWebhook","summary":"Delete a webhook","parameters":[{"name":"webhookId","in":"path","required":true,"type":"string"}],"responses":{"200":{"description":"Deleted"},"404":{"description":"Not found","schema":{"$ref":"#/definitions/Error"}}}}},"/v1/bookings":{"get":{"operationId":"ListBookings","summary":"List bookings","parameters":[{"name":"status","in":"query","type":"string","enum":["confirmed","completed","cancelled"]},{"name":"classId","in":"query","type":"string"},{"name":"from","in":"query","type":"string","format":"date-time"},{"name":"to","in":"query","type":"string","format":"date-time"},{"name":"limit","in":"query","type":"integer","maximum":50,"default":20},{"name":"startAfter","in":"query","type":"string"}],"responses":{"200":{"description":"Bookings","schema":{"$ref":"#/definitions/BookingListResponse"}}}}}},"definitions":{"Class":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"price":{"type":"number"},"maxStudents":{"type":"integer"},"enrolledStudents":{"type":"integer"},"availableSpots":{"type":"integer"},"duration":{"type":"number","description":"Hours"},"location":{"type":"string"},"dateTime":{"type":"string","format":"date-time"},"tags":{"type":"array","items":{"type":"string"}},"includedItems":{"type":"array","items":{"type":"string"}},"imageUrls":{"type":"array","items":{"type":"string"}},"rating":{"type":"number"},"reviewCount":{"type":"integer"},"isActive":{"type":"boolean"},"isTestClass":{"type":"boolean","description":"Test classes are only visible to test users, not the public."},"externalWebsiteUrl":{"type":"string","x-nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"ClassCreate":{"type":"object","required":["title","description","category","price","maxStudents","dateTime","duration","location"],"properties":{"title":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"price":{"type":"number","minimum":0},"maxStudents":{"type":"integer","minimum":1},"dateTime":{"type":"string","format":"date-time","description":"Must be in the future"},"duration":{"type":"number","description":"Hours (0-24)"},"location":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"includedItems":{"type":"array","items":{"type":"string"}},"imageUrls":{"type":"array","items":{"type":"string"}}}},"ClassUpdate":{"type":"object","description":"Any subset of the creatable fields, plus isActive.","properties":{"title":{"type":"string"},"description":{"type":"string"},"category":{"type":"string"},"price":{"type":"number","minimum":0},"maxStudents":{"type":"integer","minimum":1},"dateTime":{"type":"string","format":"date-time"},"duration":{"type":"number"},"location":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"includedItems":{"type":"array","items":{"type":"string"}},"imageUrls":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"}}},"Booking":{"type":"object","properties":{"id":{"type":"string"},"classId":{"type":"string"},"className":{"type":"string"},"studentId":{"type":"string"},"studentName":{"type":"string"},"numberOfPeople":{"type":"integer"},"status":{"type":"string","enum":["confirmed","completed","cancelled"]},"price":{"type":"number"},"attended":{"type":"boolean","x-nullable":true},"classDateTime":{"type":"string","format":"date-time"},"bookedAt":{"type":"string","format":"date-time"}}},"ClassResponse":{"type":"object","properties":{"data":{"$ref":"#/definitions/Class"}}},"ClassListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/definitions/Class"}},"nextCursor":{"type":"string","x-nullable":true}}},"BookingListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/definitions/Booking"}},"nextCursor":{"type":"string","x-nullable":true}}},"MeResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"hostId":{"type":"string"},"hostName":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}}}}},"MessageSend":{"type":"object","required":["text"],"properties":{"text":{"type":"string","maxLength":2000}}},"DirectMessageSend":{"type":"object","required":["studentId","text"],"properties":{"studentId":{"type":"string"},"text":{"type":"string","maxLength":2000},"classId":{"type":"string","description":"Optional class context"}}},"MessageResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"messageId":{"type":"string"},"studentId":{"type":"string"}}}}},"BroadcastResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"sent":{"type":"integer"},"messages":{"type":"array","items":{"type":"object","properties":{"studentId":{"type":"string"},"messageId":{"type":"string"}}}}}}}},"WebhookCreate":{"type":"object","required":["url","events"],"properties":{"url":{"type":"string","description":"https URL to receive event POSTs"},"events":{"type":"array","items":{"type":"string","enum":["booking.created","booking.cancelled"]}}}},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"active":{"type":"boolean"},"failureCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"lastDeliveryAt":{"type":"string","format":"date-time","x-nullable":true},"lastDeliveryStatus":{"type":"integer","x-nullable":true},"secret":{"type":"string","description":"Only present in the create response"}}},"WebhookResponse":{"type":"object","properties":{"data":{"$ref":"#/definitions/Webhook"}}},"WebhookListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/definitions/Webhook"}}}},"SummaryResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"classes":{"type":"object","properties":{"total":{"type":"integer"},"active":{"type":"integer"},"past":{"type":"integer"}}},"students":{"type":"object","properties":{"total":{"type":"integer"},"active":{"type":"integer"}}},"bookings":{"type":"object","properties":{"total":{"type":"integer"},"confirmed":{"type":"integer"},"completed":{"type":"integer"},"cancelled":{"type":"integer"}}},"attendance":{"type":"object","properties":{"attended":{"type":"integer"},"rate":{"type":"number"}}},"rating":{"type":"object","properties":{"average":{"type":"number"},"reviewCount":{"type":"integer"}}},"revenue":{"type":"object","properties":{"earned":{"type":"number"},"available":{"type":"number"},"pending":{"type":"number"},"currency":{"type":"string"}}}}}}},"RevenueResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"totalEarnings":{"type":"number"},"availableBalance":{"type":"number"},"pendingBalance":{"type":"number"},"totalPaidOut":{"type":"number"},"totalBookings":{"type":"integer"},"totalClassesTaught":{"type":"integer"},"lastPayoutDate":{"type":"string","format":"date-time","x-nullable":true},"earningsByMonth":{"type":"object","additionalProperties":{"type":"number"}},"lastUpdated":{"type":"string","format":"date-time","x-nullable":true},"currency":{"type":"string"}}}}},"Payout":{"type":"object","properties":{"id":{"type":"string"},"amount":{"type":"number"},"currency":{"type":"string"},"status":{"type":"string","enum":["pending","processing","completed","failed","cancelled"]},"payoutMethod":{"type":"string","x-nullable":true},"bankAccountLast4":{"type":"string","x-nullable":true},"requestedAt":{"type":"string","format":"date-time","x-nullable":true},"processedAt":{"type":"string","format":"date-time","x-nullable":true},"completedAt":{"type":"string","format":"date-time","x-nullable":true}}},"PayoutListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/definitions/Payout"}},"nextCursor":{"type":"string","x-nullable":true}}},"ClassReport":{"type":"object","properties":{"classId":{"type":"string"},"title":{"type":"string"},"dateTime":{"type":"string","format":"date-time"},"isActive":{"type":"boolean"},"maxStudents":{"type":"integer"},"enrolledStudents":{"type":"integer"},"fillRate":{"type":"number"},"bookings":{"type":"object","properties":{"confirmed":{"type":"integer"},"cancelled":{"type":"integer"}}},"attendance":{"type":"object","properties":{"attended":{"type":"integer"},"rate":{"type":"number"}}},"revenue":{"type":"number"},"rating":{"type":"number"},"reviewCount":{"type":"integer"}}},"ClassReportResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/definitions/ClassReport"}}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}