{"openapi":"3.1.0","info":{"title":"RotaHog API","version":"1.0.0","description":"Manage team rotation schedules, view upcoming turns, and perform actions like completing, substituting, or cancelling turns."},"servers":[{"url":"https://rotahog.com/api/v1"}],"security":[{"bearerAuth":[]}],"paths":{"/rotations":{"get":{"operationId":"listRotations","summary":"List rotations","description":"List all rotations the authenticated user owns or participates in.","parameters":[{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","schema":{"type":"integer","default":25,"maximum":100}}],"responses":{"200":{"description":"List of rotations","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotationsResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/rotations/{token}":{"get":{"operationId":"getRotation","summary":"Get rotation details","description":"Get a rotation's details including members and next upcoming turn.","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Rotation token"}],"responses":{"200":{"description":"Rotation details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotationDetailResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/rotations/{token}/turns":{"get":{"operationId":"listTurns","summary":"List turns","description":"List turns for a rotation with optional filters.","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Rotation token"},{"name":"status","in":"query","schema":{"type":"string","enum":["upcoming","overdue","completed","cancelled","missed"]}},{"name":"from","in":"query","schema":{"type":"string","format":"date"},"description":"Start date (ISO 8601)"},{"name":"to","in":"query","schema":{"type":"string","format":"date"},"description":"End date (ISO 8601)"},{"name":"page","in":"query","schema":{"type":"integer","default":1}},{"name":"per_page","in":"query","schema":{"type":"integer","default":25,"maximum":100}}],"responses":{"200":{"description":"List of turns","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnsResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/rotations/{token}/turns/current":{"get":{"operationId":"getCurrentTurn","summary":"Get current turn","description":"Get the next upcoming turn — who's on duty now.","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Rotation token"}],"responses":{"200":{"description":"Current turn","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/rotations/{token}/turns/{id}/complete":{"post":{"operationId":"completeTurn","summary":"Complete a turn","description":"Mark a turn as completed. Requires read_write scope. Only the rotation owner or acting member can complete.","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Rotation token"},{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Turn ID"}],"responses":{"200":{"description":"Turn completed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/UnprocessableEntity"}}}},"/rotations/{token}/turns/{id}/substitute":{"post":{"operationId":"substituteTurn","summary":"Substitute a turn","description":"Assign a different member to a turn. Requires read_write scope and the 'Find a substitute' plan feature.","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Rotation token"},{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Turn ID"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["substitute_member_id"],"properties":{"substitute_member_id":{"type":"integer","description":"ID of the member who will take over"}}}}}},"responses":{"200":{"description":"Turn substituted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/UnprocessableEntity"}}}},"/rotations/{token}/turns/{id}/cancel":{"post":{"operationId":"cancelTurn","summary":"Cancel a turn","description":"Cancel an upcoming turn. Requires read_write scope. Only the rotation owner can cancel.","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"},"description":"Rotation token"},{"name":"id","in":"path","required":true,"schema":{"type":"integer"},"description":"Turn ID"}],"responses":{"200":{"description":"Turn cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TurnResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API token created from the RotaHog API Tokens page. Prefix: rh_"}},"schemas":{"Rotation":{"type":"object","properties":{"token":{"type":"string"},"task_name":{"type":"string"},"recurrence":{"type":"string","enum":["daily","weekdays","weekly","bi_weekly","monthly"]},"start_date":{"type":"string","format":"date"},"timezone":{"type":"string"},"members_count":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}}},"RotationDetail":{"allOf":[{"$ref":"#/components/schemas/Rotation"},{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/Member"}},"next_turn":{"$ref":"#/components/schemas/TurnSummary","nullable":true}}}]},"Member":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string","nullable":true},"slack_handle":{"type":"string","nullable":true}}},"Turn":{"type":"object","properties":{"id":{"type":"integer"},"scheduled_date":{"type":"string","format":"date"},"status":{"type":"string","enum":["upcoming","overdue","completed","cancelled","missed"]},"completable":{"type":"boolean"},"acting_member":{"$ref":"#/components/schemas/Member","nullable":true},"original_member":{"$ref":"#/components/schemas/Member","nullable":true},"backup_member":{"$ref":"#/components/schemas/Member","nullable":true},"substituted_at":{"type":"string","format":"date-time","nullable":true},"backup_assigned_at":{"type":"string","format":"date-time","nullable":true}}},"TurnSummary":{"type":"object","properties":{"id":{"type":"integer"},"scheduled_date":{"type":"string","format":"date"},"status":{"type":"string"},"acting_member":{"type":"string","nullable":true}}},"Pagination":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"has_more":{"type":"boolean"}}},"RotationsResponse":{"type":"object","properties":{"rotations":{"type":"array","items":{"$ref":"#/components/schemas/Rotation"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"RotationDetailResponse":{"type":"object","properties":{"rotation":{"$ref":"#/components/schemas/RotationDetail"}}},"TurnsResponse":{"type":"object","properties":{"turns":{"type":"array","items":{"$ref":"#/components/schemas/Turn"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}},"TurnResponse":{"type":"object","properties":{"turn":{"$ref":"#/components/schemas/Turn"}}},"Error":{"type":"object","properties":{"error":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]}}}},"responses":{"Unauthorized":{"description":"Missing or invalid API token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Insufficient permissions or plan","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}