{"openapi":"3.1.0","info":{"title":"Memvera","version":"0.0.1"},"servers":[{"url":"http://localhost/api"}],"paths":{"/auth/token":{"post":{"operationId":"auth.token","summary":"Issue a Sanctum API token for valid credentials","tags":["Auth & Profile"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}},"required":["email","password"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"id":{"type":"integer"},"permissions":{"type":"array","items":{}},"brand":{"type":"string"},"modules":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{}}]},"employee_id":{"type":"string"},"is_seller":{"type":"boolean"}},"required":["token","id","permissions","brand","modules","employee_id","is_seller"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/auth/me":{"get":{"operationId":"auth.me","summary":"Re-fetch the current identity (permissions + pinned brand) for an existing token","tags":["Auth & Profile"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"permissions":{"type":"array","items":{}},"brand":{"type":"string"},"modules":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{}}]},"employee_id":{"type":"string"},"is_seller":{"type":"boolean"},"token_abilities":{"description":"The bearer token's Sanctum abilities \u2014 ['*'] for logins; a module list for\nscoped MCP connector tokens. The agent service filters tools by this.","anyOf":[{"type":"string"},{"type":"array","prefixItems":[{"type":"string","const":"*"}],"minItems":1,"maxItems":1,"additionalItems":false}]},"token_id":{"type":"string"},"token_name":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]}},"required":["id","permissions","brand","modules","employee_id","is_seller","token_abilities","token_id","token_name"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/profile":{"get":{"operationId":"profile.show","tags":["Auth & Profile"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"},"avatar_url":{"type":["string","null"]},"status":{"type":"string"},"brand":{"type":"string"},"last_login_at":{"type":"string"},"roles":{"type":"array","items":{}}},"required":["id","name","email","avatar_url","status","brand","last_login_at","roles"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"profile.update","tags":["Auth & Profile"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"email":{"type":"string","format":"email","maxLength":190}},"required":["name","email"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"},"avatar_url":{"type":["string","null"]},"status":{"type":"string"},"brand":{"type":"string"},"last_login_at":{"type":"string"},"roles":{"type":"array","items":{}}},"required":["id","name","email","avatar_url","status","brand","last_login_at","roles"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/profile/commissions":{"get":{"operationId":"profile.commissions","summary":"GET /profile/commissions \u2014 the signed-in staff member's own commission ledger, split into paid\n(settled in a payout) and not-yet-paid (posted, awaiting a payout run). Clawbacks net into the\nright bucket by their paid_at. User-level (no X-Brand) \u2014 scoped by the user's own employee id, so\nqueried without global scopes; an unlinked user (no employee) simply has none","tags":["Auth & Profile"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"is_seller":{"type":"boolean"},"paid_minor":{"type":"integer"},"unpaid_minor":{"type":"integer"},"currency":{"type":"string"},"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"amount_minor":{"type":"integer"},"paid":{"type":"boolean"},"clawback":{"type":"string"},"contract_id":{"type":"string"},"posted_at":{"type":"string"},"paid_at":{"type":"string"}},"required":["id","source","amount_minor","paid","clawback","contract_id","posted_at","paid_at"]}}},"required":["is_seller","paid_minor","unpaid_minor","currency","rows"]},{"type":"object","properties":{"is_seller":{"type":"boolean"},"paid_minor":{"type":"integer"},"unpaid_minor":{"type":"integer"},"currency":{"type":"string","const":"SAR"},"rows":{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}},"required":["is_seller","paid_minor","unpaid_minor","currency","rows"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/profile/password":{"post":{"operationId":"profile.password","tags":["Auth & Profile"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"current_password":{"type":"string"},"password":{"type":"string","minLength":8},"password_confirmation":{"type":"string","minLength":8}},"required":["current_password","password","password_confirmation"]}}}},"responses":{"200":{"description":"re-hashed by the model cast","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/profile/avatar":{"post":{"operationId":"profile.avatar","tags":["Auth & Profile"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string","description":"A small, frontend-resized data URL (PNG/JPEG/WebP). ~2M chars \u2248 1.5MB binary.","pattern":"#^data:image/(png|jpe?g|webp);base64,#","maxLength":2000000}},"required":["image"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"},"avatar_url":{"type":["string","null"]},"status":{"type":"string"},"brand":{"type":"string"},"last_login_at":{"type":"string"},"roles":{"type":"array","items":{}}},"required":["id","name","email","avatar_url","status","brand","last_login_at","roles"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"profile.deleteAvatar","tags":["Auth & Profile"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"},"avatar_url":{"type":["string","null"]},"status":{"type":"string"},"brand":{"type":"string"},"last_login_at":{"type":"string"},"roles":{"type":"array","items":{}}},"required":["id","name","email","avatar_url","status","brand","last_login_at","roles"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/trainer-statements/{employee}":{"get":{"operationId":"trainer.statement","summary":"Signed public render (the app opens it in a browser; print \u2192 PDF)","tags":["Commissions & Targets"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}},{"name":"period","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":""}},"required":["message"]}}}}}}},"/manage/role-commission-policies":{"get":{"operationId":"roleCommission.policies","summary":"Both policies (creates defaults on first read)","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"supervisor":{"$ref":"#/components/schemas/SupervisorCommissionPolicy"},"coach":{"$ref":"#/components/schemas/CoachCommissionPolicy"}},"required":["supervisor","coach"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/role-commission-policies/supervisor":{"put":{"operationId":"roleCommission.updateSupervisor","tags":["Commissions & Targets"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"personal_gate_new_sales":{"type":"integer","minimum":0},"personal_band1_min":{"type":"integer","minimum":0},"personal_band1_rate":{"type":"number","minimum":0,"maximum":100},"personal_band2_min":{"type":"integer","minimum":0},"personal_band2_rate":{"type":"number","minimum":0,"maximum":100},"branch_gate_new_sales":{"type":"integer","minimum":0},"branch_gate_renewal_rate":{"type":"number","minimum":0,"maximum":100},"branch_band1_min":{"type":"integer","minimum":0},"branch_band1_rate":{"type":"number","minimum":0,"maximum":100},"branch_band2_min":{"type":"integer","minimum":0},"branch_band2_rate":{"type":"number","minimum":0,"maximum":100},"renewal_band1_min_rate":{"type":"number","minimum":0,"maximum":100},"renewal_band1_rate":{"type":"number","minimum":0,"maximum":100},"renewal_band2_min_rate":{"type":"number","minimum":0,"maximum":100},"renewal_band2_rate":{"type":"number","minimum":0,"maximum":100}}}}}},"responses":{"200":{"description":"`SupervisorCommissionPolicy`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupervisorCommissionPolicy"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/role-commission-policies/coach":{"put":{"operationId":"roleCommission.updateCoach","tags":["Commissions & Targets"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"ft_same_day_amount":{"type":"integer","minimum":0},"ft_within_days":{"type":"integer","minimum":0,"maximum":365},"ft_within_amount":{"type":"integer","minimum":0},"pt_attended_amount":{"type":"integer","minimum":0},"pt_noshow_amount":{"type":"integer","minimum":0}}}}}},"responses":{"200":{"description":"`CoachCommissionPolicy`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoachCommissionPolicy"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/sales-commission-rules":{"get":{"operationId":"salesCommission.rules","summary":"GET /manage/sales-commission-rules \u2014 every commission rule (with employee names) + employees + types","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_type":{"type":"string"},"employee_id":{"type":["string","null"]},"employee_name":{"anyOf":[{"type":"string"},{"type":"null"},{"type":"string","enum":["Unknown"]}]},"rate_percent":{"type":"number"},"fixed_minor":{"type":"integer"},"effective_from":{"type":"string"},"effective_to":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","member_type","employee_id","employee_name","rate_percent","fixed_minor","effective_from","effective_to","is_active"]}},"employees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"types":{"type":"array","prefixItems":[{"type":"string","const":"any"},{"type":"string","const":"new"},{"type":"string","const":"renew"}],"minItems":3,"maxItems":3,"additionalItems":false}},"required":["rules","employees","types"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"salesCommission.storeRule","summary":"POST /manage/sales-commission-rules \u2014 add a rule (per employee / period)","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_type":{"type":"string"},"employee_id":{"type":["string","null"]},"employee_name":{"anyOf":[{"type":"string"},{"type":"null"},{"type":"string","enum":["Unknown"]}]},"rate_percent":{"type":"number"},"fixed_minor":{"type":"integer"},"effective_from":{"type":"string"},"effective_to":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","member_type","employee_id","employee_name","rate_percent","fixed_minor","effective_from","effective_to","is_active"]}},"employees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"types":{"type":"array","prefixItems":[{"type":"string","const":"any"},{"type":"string","const":"new"},{"type":"string","const":"renew"}],"minItems":3,"maxItems":3,"additionalItems":false}},"required":["rules","employees","types"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/sales-commission-rules/preview":{"get":{"operationId":"salesCommission.preview","summary":"GET /manage/sales-commission-rules/preview \u2014 what a sale would earn under the current rules","tags":["Commissions & Targets"],"parameters":[{"name":"member_type","in":"query","required":true,"schema":{"type":"string","enum":["new","renew"]}},{"name":"employee_id","in":"query","schema":{"type":["string","null"]}},{"name":"date","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"amount_minor","in":"query","required":true,"schema":{"type":"integer","minimum":0}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"resolved":{"type":"boolean"},"rate_percent":{"type":["number","null"]},"fixed_minor":{"type":["integer","null"]},"commission_minor":{"type":"integer","description":"amount_minor arrives as a string from the GET query \u2014 cast before the int-typed compute (strict_types)."}},"required":["resolved","rate_percent","fixed_minor","commission_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/sales-commission-rules/{rule}":{"patch":{"operationId":"salesCommission.updateRule","summary":"PATCH /manage/sales-commission-rules/{rule} \u2014 edit a rule","tags":["Commissions & Targets"],"parameters":[{"name":"rule","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_type":{"type":"string"},"employee_id":{"type":["string","null"]},"employee_name":{"anyOf":[{"type":"string"},{"type":"null"},{"type":"string","enum":["Unknown"]}]},"rate_percent":{"type":"number"},"fixed_minor":{"type":"integer"},"effective_from":{"type":"string"},"effective_to":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","member_type","employee_id","employee_name","rate_percent","fixed_minor","effective_from","effective_to","is_active"]}},"employees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"types":{"type":"array","prefixItems":[{"type":"string","const":"any"},{"type":"string","const":"new"},{"type":"string","const":"renew"}],"minItems":3,"maxItems":3,"additionalItems":false}},"required":["rules","employees","types"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"salesCommission.deleteRule","summary":"DELETE /manage/sales-commission-rules/{rule} \u2014 remove a rule","tags":["Commissions & Targets"],"parameters":[{"name":"rule","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_type":{"type":"string"},"employee_id":{"type":["string","null"]},"employee_name":{"anyOf":[{"type":"string"},{"type":"null"},{"type":"string","enum":["Unknown"]}]},"rate_percent":{"type":"number"},"fixed_minor":{"type":"integer"},"effective_from":{"type":"string"},"effective_to":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","member_type","employee_id","employee_name","rate_percent","fixed_minor","effective_from","effective_to","is_active"]}},"employees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"types":{"type":"array","prefixItems":[{"type":"string","const":"any"},{"type":"string","const":"new"},{"type":"string","const":"renew"}],"minItems":3,"maxItems":3,"additionalItems":false}},"required":["rules","employees","types"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-split":{"get":{"operationId":"salesCommission.splitPolicy","summary":"GET /manage/commission-split \u2014 the brand's primary/assist split policy","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"assist_pct":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["assist_pct","is_active"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"salesCommission.saveSplitPolicy","summary":"PUT /manage/commission-split \u2014 upsert the brand's split policy","tags":["Commissions & Targets"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assist_pct":{"type":"integer","minimum":0,"maximum":100},"is_active":{"type":"boolean"}},"required":["assist_pct","is_active"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"assist_pct":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["assist_pct","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-targets":{"get":{"operationId":"commissionTarget.index","summary":"GET /manage/commission-targets?month=YYYY-MM \u2014 tiers + per-rep targets/achievement for the month","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"month":{"type":"string"},"tiers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"from_pct":{"type":"integer"},"rate_multiplier":{"type":"number"}},"required":["id","from_pct","rate_multiplier"]}},"targets":{"type":"array","items":{"type":"object","properties":{"employee_id":{"type":"string"},"name":{"type":"string"},"target_amount_minor":{"type":"string"},"target_count":{"type":"string"},"achievement_pct":{"type":["number","null"]},"multiplier":{"type":"number"}},"required":["employee_id","name","target_amount_minor","target_count","achievement_pct","multiplier"]}}},"required":["month","tiers","targets"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/sales-targets":{"put":{"operationId":"commissionTarget.saveTarget","summary":"PUT /manage/sales-targets \u2014 upsert a rep's monthly target (clears it when both metrics are empty)","tags":["Commissions & Targets"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employee_id":{"type":"string"},"month":{"type":"string"},"target_amount_minor":{"type":["integer","null"],"minimum":0},"target_count":{"type":["integer","null"],"minimum":0}},"required":["employee_id","month"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"month":{"type":"string"},"tiers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"from_pct":{"type":"integer"},"rate_multiplier":{"type":"number"}},"required":["id","from_pct","rate_multiplier"]}},"targets":{"type":"array","items":{"type":"object","properties":{"employee_id":{"type":"string"},"name":{"type":"string"},"target_amount_minor":{"type":"string"},"target_count":{"type":"string"},"achievement_pct":{"type":["number","null"]},"multiplier":{"type":"number"}},"required":["employee_id","name","target_amount_minor","target_count","achievement_pct","multiplier"]}}},"required":["month","tiers","targets"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/branch-targets":{"get":{"operationId":"branchTarget.index","summary":"GET /manage/branch-targets?month=YYYY-MM \u2014 every active club with target, fallback and live actuals","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"month":{"type":"string"},"clubs":{"type":"array","items":{"type":"object","properties":{"club_id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"target":{"type":["object","null"],"properties":{"new_count":{"type":"string"},"renewal_count":{"type":"string"},"amount_minor":{"type":"string"}},"required":["new_count","renewal_count","amount_minor"]},"fallback":{"type":"object","properties":{"count":{"type":"integer"},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]},"actual":{"type":"object","properties":{"new_count":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"renewal_count":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"amount_minor":{"type":"integer"}},"required":["new_count","renewal_count","amount_minor"]},"achieved_new_pct":{"type":["number","null"]},"achieved_renewal_pct":{"type":["number","null"]},"achieved_amount_pct":{"type":["number","null"]}},"required":["club_id","name","name_ar","target","fallback","actual","achieved_new_pct","achieved_renewal_pct","achieved_amount_pct"]}}},"required":["month","clubs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"branchTarget.save","summary":"PUT /manage/branch-targets \u2014 upsert one club-month target (all-zero clears the row \u2192 fallback)","tags":["Commissions & Targets"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"month":{"type":"string"},"new_count":{"type":["integer","null"],"minimum":0},"renewal_count":{"type":["integer","null"],"minimum":0},"amount_minor":{"type":["integer","null"],"minimum":0}},"required":["club_id","month"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"month":{"type":"string"},"clubs":{"type":"array","items":{"type":"object","properties":{"club_id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"target":{"type":["object","null"],"properties":{"new_count":{"type":"string"},"renewal_count":{"type":"string"},"amount_minor":{"type":"string"}},"required":["new_count","renewal_count","amount_minor"]},"fallback":{"type":"object","properties":{"count":{"type":"integer"},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]},"actual":{"type":"object","properties":{"new_count":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"renewal_count":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"amount_minor":{"type":"integer"}},"required":["new_count","renewal_count","amount_minor"]},"achieved_new_pct":{"type":["number","null"]},"achieved_renewal_pct":{"type":["number","null"]},"achieved_amount_pct":{"type":["number","null"]}},"required":["club_id","name","name_ar","target","fallback","actual","achieved_new_pct","achieved_renewal_pct","achieved_amount_pct"]}}},"required":["month","clubs"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/branch-targets/copy":{"post":{"operationId":"branchTarget.copyPrevious","summary":"POST /manage/branch-targets/copy \u2014 copy the previous month's targets in (clubs without a row only)","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"month":{"type":"string"},"clubs":{"type":"array","items":{"type":"object","properties":{"club_id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"target":{"type":["object","null"],"properties":{"new_count":{"type":"string"},"renewal_count":{"type":"string"},"amount_minor":{"type":"string"}},"required":["new_count","renewal_count","amount_minor"]},"fallback":{"type":"object","properties":{"count":{"type":"integer"},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]},"actual":{"type":"object","properties":{"new_count":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"renewal_count":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"amount_minor":{"type":"integer"}},"required":["new_count","renewal_count","amount_minor"]},"achieved_new_pct":{"type":["number","null"]},"achieved_renewal_pct":{"type":["number","null"]},"achieved_amount_pct":{"type":["number","null"]}},"required":["club_id","name","name_ar","target","fallback","actual","achieved_new_pct","achieved_renewal_pct","achieved_amount_pct"]}}},"required":["month","clubs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-payouts":{"get":{"operationId":"commissionPayout.index","summary":"GET /manage/commission-payouts \u2014 the brand's payout batches, newest first","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payouts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"period_from":{"type":"string"},"period_to":{"type":"string"},"status":{"type":"string"},"total_minor":{"type":"integer"},"employee_count":{"type":"integer"},"currency":{"type":"string"},"reference":{"type":["string","null"]},"approved_at":{"type":"string"},"paid_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","period_from","period_to","status","total_minor","employee_count","currency","reference","approved_at","paid_at","created_at"]}},"currency":{"type":"string","const":"SAR"}},"required":["payouts","currency"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"commissionPayout.store","summary":"POST /manage/commission-payouts \u2014 create a draft run over [from, to]","tags":["Commissions & Targets"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"period_from":{"type":"string"},"period_to":{"type":"string"},"status":{"type":"string"},"total_minor":{"type":"integer"},"employee_count":{"type":"integer"},"currency":{"type":"string"},"reference":{"type":["string","null"]},"approved_at":{"type":"string"},"paid_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","period_from","period_to","status","total_minor","employee_count","currency","reference","approved_at","paid_at","created_at"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"No unpaid commissions in this period."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-payouts/preview":{"get":{"operationId":"commissionPayout.preview","summary":"GET /manage/commission-payouts/preview?from&to \u2014 what a run over this period would pay","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-payouts/{payout}":{"get":{"operationId":"commissionPayout.show","summary":"GET /manage/commission-payouts/{payout} \u2014 a payout with its per-employee lines","tags":["Commissions & Targets"],"parameters":[{"name":"payout","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-payouts/{payout}/export":{"get":{"operationId":"commissionPayout.export","summary":"GET /manage/commission-payouts/{payout}/export \u2014 per-employee payroll CSV","tags":["Commissions & Targets"],"parameters":[{"name":"payout","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"text/csv; charset=UTF-8":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-payouts/{payout}/approve":{"post":{"operationId":"commissionPayout.approve","summary":"POST /manage/commission-payouts/{payout}/approve","tags":["Commissions & Targets"],"parameters":[{"name":"payout","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"period_from":{"type":"string"},"period_to":{"type":"string"},"status":{"type":"string"},"total_minor":{"type":"integer"},"employee_count":{"type":"integer"},"currency":{"type":"string"},"reference":{"type":["string","null"]},"approved_at":{"type":"string"},"paid_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","period_from","period_to","status","total_minor","employee_count","currency","reference","approved_at","paid_at","created_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-payouts/{payout}/mark-paid":{"post":{"operationId":"commissionPayout.markPaid","summary":"POST /manage/commission-payouts/{payout}/mark-paid \u2014 settle it (payroll reference optional)","tags":["Commissions & Targets"],"parameters":[{"name":"payout","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reference":{"type":["string","null"],"maxLength":120}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"period_from":{"type":"string"},"period_to":{"type":"string"},"status":{"type":"string"},"total_minor":{"type":"integer"},"employee_count":{"type":"integer"},"currency":{"type":"string"},"reference":{"type":["string","null"]},"approved_at":{"type":"string"},"paid_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","period_from","period_to","status","total_minor","employee_count","currency","reference","approved_at","paid_at","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-tiers":{"get":{"operationId":"commissionTier.index","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tiers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"rate_percent":{"type":"integer"},"commissionable_sessions_limit":{"type":["integer","null"]},"limit_period":{"type":"string"},"is_active":{"type":"boolean"},"trainer_ids":{"type":"object","additionalProperties":{}},"trainer_count":{"type":"integer"}},"required":["id","name","rate_percent","commissionable_sessions_limit","limit_period","is_active","trainer_ids","trainer_count"]}}},"required":["tiers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"commissionTier.store","tags":["Commissions & Targets"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"rate_percent":{"type":"integer"},"commissionable_sessions_limit":{"type":["integer","null"]},"limit_period":{"type":"string"},"is_active":{"type":"boolean"},"trainer_ids":{"type":"object","additionalProperties":{}},"trainer_count":{"type":"integer"}},"required":["id","name","rate_percent","commissionable_sessions_limit","limit_period","is_active","trainer_ids","trainer_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-tiers/{tier}":{"patch":{"operationId":"commissionTier.update","tags":["Commissions & Targets"],"parameters":[{"name":"tier","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"rate_percent":{"type":"integer"},"commissionable_sessions_limit":{"type":["integer","null"]},"limit_period":{"type":"string"},"is_active":{"type":"boolean"},"trainer_ids":{"type":"object","additionalProperties":{}},"trainer_count":{"type":"integer"}},"required":["id","name","rate_percent","commissionable_sessions_limit","limit_period","is_active","trainer_ids","trainer_count"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"commissionTier.destroy","tags":["Commissions & Targets"],"parameters":[{"name":"tier","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-rate-rules":{"get":{"operationId":"commissionRateRule.index","tags":["Commissions & Targets"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"commission_tier_id":{"type":["string","null"]},"effective_from":{"type":"string"},"effective_to":{"type":"string"},"member_type":{"type":"string"},"rate_percent":{"type":"integer"},"priority":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","commission_tier_id","effective_from","effective_to","member_type","rate_percent","priority","is_active"]}}},"required":["rules"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"commissionRateRule.store","tags":["Commissions & Targets"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"commission_tier_id":{"type":["string","null"]},"effective_from":{"type":"string"},"effective_to":{"type":"string"},"member_type":{"type":"string"},"rate_percent":{"type":"integer"},"priority":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","commission_tier_id","effective_from","effective_to","member_type","rate_percent","priority","is_active"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-rate-rules/bulk":{"post":{"operationId":"commissionRateRule.bulk","summary":"Bulk save the rate matrix in one transaction: upsert (id present = update, absent = create) and\ndelete cleared cells. Brand-scoped throughout, so cross-brand ids are ignored","tags":["Commissions & Targets"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"delete":{"type":"array","items":{"type":"string"}},"upsert":{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"]},"commission_tier_id":{"type":["string","null"]},"effective_from":{"type":["string","null"],"format":"date-time"},"effective_to":{"type":["string","null"],"format":"date-time"},"member_type":{"type":"string","enum":["any","new","renew"]},"rate_percent":{"type":"integer","minimum":0,"maximum":100},"priority":{"type":"integer","minimum":0},"is_active":{"type":"boolean"}},"required":["rate_percent"]}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"commission_tier_id":{"type":["string","null"]},"effective_from":{"type":"string"},"effective_to":{"type":"string"},"member_type":{"type":"string"},"rate_percent":{"type":"integer"},"priority":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","commission_tier_id","effective_from","effective_to","member_type","rate_percent","priority","is_active"]}}},"required":["rules"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-rate-rules/{rule}":{"patch":{"operationId":"commissionRateRule.update","tags":["Commissions & Targets"],"parameters":[{"name":"rule","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"commission_tier_id":{"type":["string","null"]},"effective_from":{"type":"string"},"effective_to":{"type":"string"},"member_type":{"type":"string"},"rate_percent":{"type":"integer"},"priority":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","commission_tier_id","effective_from","effective_to","member_type","rate_percent","priority","is_active"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"commissionRateRule.destroy","tags":["Commissions & Targets"],"parameters":[{"name":"rule","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/login":{"post":{"operationId":"corporateAuth.login","tags":["Corporate B2B"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}},"required":["email","password"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"company":{"type":"string"}},"required":["token","company"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/corp/company":{"get":{"operationId":"corporatePortal.company","summary":"GET /corp/company \u2014 the account's contract terms + billing summary","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"brand":{"type":"string"},"contract_id":{"type":"string"},"plan":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"discount_pct":{"type":"integer"},"quota":{"type":"integer"},"seats_used":{"type":"integer","minimum":0},"enrollment_code":{},"contract_start":{"type":"string"},"contract_end":{"type":"string"},"billing_contact":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":["string","null"]}},"required":["name","email","phone"]},"credit_limit_minor":{"type":"integer"},"credit_days":{"type":"integer"},"outstanding_minor":{"type":"integer"}},"required":["name","brand","contract_id","plan","discount_pct","quota","seats_used","enrollment_code","contract_start","contract_end","billing_contact","credit_limit_minor","credit_days","outstanding_minor"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/roster":{"get":{"operationId":"corporatePortal.roster","summary":"GET /corp/roster \u2014 the company's employees","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"employees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"employee_id":{"type":["string","null"]},"status":{"type":"string"},"joined_at":{"type":"string"},"plan":{"type":["string","null"]},"department_id":{"type":["string","null"]},"department_name":{"type":"string"}},"required":["id","name","email","employee_id","status","joined_at","plan","department_id","department_name"]}}},"required":["employees"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"corporatePortal.inviteEmployee","summary":"POST /corp/roster \u2014 invite an employee (enters \"invited\" until self-sign-up + KYC)","tags":["Corporate B2B"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":160},"email":{"type":"string","format":"email","maxLength":190},"employee_id":{"type":["string","null"],"maxLength":60},"plan":{"type":["string","null"],"maxLength":120}},"required":["name","email"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"employee_id":{"type":["string","null"]},"status":{"type":"string"},"joined_at":{"type":"string"},"plan":{"type":["string","null"]},"department_id":{"type":["string","null"]},"department_name":{"type":"string"}},"required":["id","name","email","employee_id","status","joined_at","plan","department_id","department_name"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/roster/{employee}":{"delete":{"operationId":"corporatePortal.removeEmployee","summary":"DELETE /corp/roster/{employee} \u2014 deactivate an employee (frees the seat)","tags":["Corporate B2B"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"employees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"employee_id":{"type":["string","null"]},"status":{"type":"string"},"joined_at":{"type":"string"},"plan":{"type":["string","null"]},"department_id":{"type":["string","null"]},"department_name":{"type":"string"}},"required":["id","name","email","employee_id","status","joined_at","plan","department_id","department_name"]}}},"required":["employees"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/invoices":{"get":{"operationId":"corporatePortal.invoices","summary":"GET /corp/invoices \u2014 the account's monthly B2B invoices","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"period_start":{"type":"string"},"period_end":{"type":"string"},"amount_minor":{"type":"integer"},"balance_minor":{"type":"integer"},"status":{"type":"string"},"issued_at":{"type":"string"},"due_at":{"type":"string"},"paid_at":{"type":"string"}},"required":["id","number","period_start","period_end","amount_minor","balance_minor","status","issued_at","due_at","paid_at"]}}},"required":["invoices"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/invoices/{invoice}/pay":{"post":{"operationId":"corporatePortal.payInvoice","summary":"POST /corp/invoices/{invoice}/pay \u2014 HR pays an invoice online; returns the gateway redirect","tags":["Corporate B2B"],"parameters":[{"name":"invoice","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"redirect_url":{"type":["string","null"]}},"required":["payment_id","redirect_url"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"already_paid"}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/departments":{"get":{"operationId":"corporatePortal.departments","summary":"GET /corp/departments \u2014 the company's cost-centers","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"departments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"code":{"type":["string","null"]}},"required":["id","name","name_ar","code"]}}},"required":["departments"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"corporatePortal.storeDepartment","tags":["Corporate B2B"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"name_ar":{"type":["string","null"],"maxLength":80},"code":{"type":["string","null"],"maxLength":40}},"required":["name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/departments/{department}":{"delete":{"operationId":"corporatePortal.destroyDepartment","tags":["Corporate B2B"],"parameters":[{"name":"department","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/roster/{employee}/department":{"patch":{"operationId":"corporatePortal.assignDepartment","summary":"PATCH /corp/roster/{employee}/department \u2014 assign an employee to a cost-center","tags":["Corporate B2B"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"department_id":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"employee_id":{"type":["string","null"]},"status":{"type":"string"},"joined_at":{"type":"string"},"plan":{"type":["string","null"]},"department_id":{"type":["string","null"]},"department_name":{"type":"string"}},"required":["id","name","email","employee_id","status","joined_at","plan","department_id","department_name"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/spend":{"get":{"operationId":"corporatePortal.spend","summary":"GET /corp/spend \u2014 per-department headcount + employer spend","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"departments":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"code":{"type":"string"},"headcount":{"type":"integer"},"spend_minor":{"type":"integer"}},"required":["id","name","name_ar","code","headcount","spend_minor"]},{"type":"object","properties":{"id":{"type":"null"},"name":{"type":"string","const":"Unassigned"},"name_ar":{"type":"string","const":"\u063a\u064a\u0631 \u0645\u064f\u0635\u0646\u0651\u0641"},"code":{"type":"null"},"headcount":{"type":"integer"},"spend_minor":{"type":"integer"}},"required":["id","name","name_ar","code","headcount","spend_minor"]}]}},"total_spend_minor":{"anyOf":[{"type":"integer"},{"type":"number"}]},"total_headcount":{"anyOf":[{"type":"integer"},{"type":"number"}]}},"required":["departments","total_spend_minor","total_headcount"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/contracts":{"get":{"operationId":"corporatePortal.contracts","summary":"GET /corp/contracts \u2014 the corporate agreement, derived from the account terms","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contracts":{"type":"array","prefixItems":[{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"status":{"type":"string","enum":["draft","expired","active"]},"start":{"type":"string"},"end":{"type":"string"},"discount_pct":{"type":"integer"},"min_threshold":{"type":["integer","null"]},"plan":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"signed_at":{"type":"string"}},"required":["id","title","status","start","end","discount_pct","min_threshold","plan","signed_at"]}],"minItems":1,"maxItems":1,"additionalItems":false}},"required":["contracts"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/documents":{"get":{"operationId":"corporatePortal.documents","summary":"GET /corp/documents \u2014 the account's document repository","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"},"employee":{"type":["string","null"]},"uploaded_at":{"type":"string"},"size_kb":{"type":"integer"}},"required":["id","name","kind","employee","uploaded_at","size_kb"]}}},"required":["documents"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/kyc/pending-employment":{"get":{"operationId":"corporatePortal.pendingEmployment","summary":"GET /corp/kyc/pending-employment \u2014 employees awaiting HR employment confirmation (RFP 1.6, step 1)","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"employees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"employee_id":{"type":["string","null"]},"status":{"type":"string"},"joined_at":{"type":"string"},"plan":{"type":["string","null"]},"department_id":{"type":["string","null"]},"department_name":{"type":"string"}},"required":["id","name","email","employee_id","status","joined_at","plan","department_id","department_name"]}}},"required":["employees"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corp/kyc/{employee}/employment":{"post":{"operationId":"corporatePortal.confirmEmployment","summary":"POST /corp/kyc/{employee}/employment \u2014 HR confirms (or rejects) employment, KYC step 1. Logs the\nHR admin's identity on the approval; on approve, opens the gym identity step + tries activation","tags":["Corporate B2B"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decision":{"type":"string","enum":["approve","reject"]},"reason":{"type":["string","null"],"maxLength":500}},"required":["decision"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"employee_id":{"type":["string","null"]},"status":{"type":"string"},"joined_at":{"type":"string"},"plan":{"type":["string","null"]},"department_id":{"type":["string","null"]},"department_name":{"type":"string"}},"required":["id","name","email","employee_id","status","joined_at","plan","department_id","department_name"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts":{"get":{"operationId":"corporateAccount.index","summary":"List the brand's corporate accounts with seats_used","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"corporate_accounts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"company_name":{"type":"string"},"company_name_ar":{"type":["string","null"]},"contact_name":{"type":"string"},"contact_email":{"type":"string"},"contact_phone":{"type":["string","null"]},"membership_plan_id":{"type":"string"},"seats":{"type":"integer"},"seats_used":{"type":"integer","minimum":0},"discount_percent":{"type":"integer"},"subsidy_percent":{"type":"integer"},"unbilled_minor":{"type":"string"},"outstanding_minor":{"type":"string"},"overdue_minor":{"type":"string"},"on_credit_hold":{"type":"string"},"is_active":{"type":"boolean"},"minimum_threshold":{"type":["integer","null"]},"enrollment_starts_at":{"type":"string"},"enrollment_ends_at":{"type":"string"},"requires_employee_id":{"type":"boolean"},"credit_limit_minor":{"type":"integer"},"credit_days":{"type":"integer"},"threshold_met":{"type":"boolean"},"enrollment_open":{"type":"boolean"},"has_admin":{"type":"boolean"},"admin_email":{"type":"string"}},"required":["id","company_name","company_name_ar","contact_name","contact_email","contact_phone","membership_plan_id","seats","seats_used","discount_percent","subsidy_percent","unbilled_minor","outstanding_minor","overdue_minor","on_credit_hold","is_active","minimum_threshold","enrollment_starts_at","enrollment_ends_at","requires_employee_id","credit_limit_minor","credit_days","threshold_met","enrollment_open","has_admin","admin_email"]}}},"required":["corporate_accounts"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"corporateAccount.store","tags":["Corporate B2B"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"company_name":{"type":"string","maxLength":160},"company_name_ar":{"type":["string","null"],"maxLength":160},"contact_name":{"type":"string","maxLength":120},"contact_email":{"type":"string","format":"email","maxLength":160},"contact_phone":{"type":["string","null"],"maxLength":30},"membership_plan_id":{"type":"string"},"seats":{"type":"integer","minimum":1},"discount_percent":{"type":"integer","minimum":0,"maximum":100},"subsidy_percent":{"type":"integer","minimum":0,"maximum":100},"is_active":{"type":"boolean"},"minimum_threshold":{"type":["integer","null"],"description":"RFP-01 #6 B2B volume-threshold & employee-ID document config.","minimum":0},"enrollment_starts_at":{"type":["string","null"],"format":"date-time"},"enrollment_ends_at":{"type":["string","null"],"format":"date-time"},"requires_employee_id":{"type":"boolean"},"credit_limit_minor":{"type":["integer","null"],"description":"RFP-24 #6 B2B billing terms.","minimum":0},"credit_days":{"type":["integer","null"],"minimum":0}},"required":["company_name","contact_name","contact_email","membership_plan_id","seats"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"company_name":{"type":"string"},"company_name_ar":{"type":["string","null"]},"contact_name":{"type":"string"},"contact_email":{"type":"string"},"contact_phone":{"type":["string","null"]},"membership_plan_id":{"type":"string"},"seats":{"type":"integer"},"seats_used":{"type":"integer","minimum":0},"discount_percent":{"type":"integer"},"subsidy_percent":{"type":"integer"},"unbilled_minor":{"type":"string"},"outstanding_minor":{"type":"string"},"overdue_minor":{"type":"string"},"on_credit_hold":{"type":"string"},"is_active":{"type":"boolean"},"minimum_threshold":{"type":["integer","null"]},"enrollment_starts_at":{"type":"string"},"enrollment_ends_at":{"type":"string"},"requires_employee_id":{"type":"boolean"},"credit_limit_minor":{"type":"integer"},"credit_days":{"type":"integer"},"threshold_met":{"type":"boolean"},"enrollment_open":{"type":"boolean"},"has_admin":{"type":"boolean"},"admin_email":{"type":"string"}},"required":["id","company_name","company_name_ar","contact_name","contact_email","contact_phone","membership_plan_id","seats","seats_used","discount_percent","subsidy_percent","unbilled_minor","outstanding_minor","overdue_minor","on_credit_hold","is_active","minimum_threshold","enrollment_starts_at","enrollment_ends_at","requires_employee_id","credit_limit_minor","credit_days","threshold_met","enrollment_open","has_admin","admin_email"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}":{"patch":{"operationId":"corporateAccount.update","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"company_name":{"type":"string","maxLength":160},"company_name_ar":{"type":["string","null"],"maxLength":160},"contact_name":{"type":"string","maxLength":120},"contact_email":{"type":"string","format":"email","maxLength":160},"contact_phone":{"type":["string","null"],"maxLength":30},"membership_plan_id":{"type":"string"},"seats":{"type":"integer","minimum":1},"discount_percent":{"type":"integer","minimum":0,"maximum":100},"subsidy_percent":{"type":"integer","minimum":0,"maximum":100},"is_active":{"type":"boolean"},"minimum_threshold":{"type":["integer","null"],"description":"RFP-01 #6 B2B volume-threshold & employee-ID document config.","minimum":0},"enrollment_starts_at":{"type":["string","null"],"format":"date-time"},"enrollment_ends_at":{"type":["string","null"],"format":"date-time"},"requires_employee_id":{"type":"boolean"},"credit_limit_minor":{"type":["integer","null"],"minimum":0},"credit_days":{"type":["integer","null"],"minimum":0}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"company_name":{"type":"string"},"company_name_ar":{"type":["string","null"]},"contact_name":{"type":"string"},"contact_email":{"type":"string"},"contact_phone":{"type":["string","null"]},"membership_plan_id":{"type":"string"},"seats":{"type":"integer"},"seats_used":{"type":"integer","minimum":0},"discount_percent":{"type":"integer"},"subsidy_percent":{"type":"integer"},"unbilled_minor":{"type":"string"},"outstanding_minor":{"type":"string"},"overdue_minor":{"type":"string"},"on_credit_hold":{"type":"string"},"is_active":{"type":"boolean"},"minimum_threshold":{"type":["integer","null"]},"enrollment_starts_at":{"type":"string"},"enrollment_ends_at":{"type":"string"},"requires_employee_id":{"type":"boolean"},"credit_limit_minor":{"type":"integer"},"credit_days":{"type":"integer"},"threshold_met":{"type":"boolean"},"enrollment_open":{"type":"boolean"},"has_admin":{"type":"boolean"},"admin_email":{"type":"string"}},"required":["id","company_name","company_name_ar","contact_name","contact_email","contact_phone","membership_plan_id","seats","seats_used","discount_percent","subsidy_percent","unbilled_minor","outstanding_minor","overdue_minor","on_credit_hold","is_active","minimum_threshold","enrollment_starts_at","enrollment_ends_at","requires_employee_id","credit_limit_minor","credit_days","threshold_met","enrollment_open","has_admin","admin_email"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/status":{"get":{"operationId":"corporateAccount.status","summary":"RFP-01 #6: B2B threshold & enrolment-window status for an account","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"seats":{"type":"string"},"seats_used":{"type":"string"},"minimum_threshold":{"type":"string"},"threshold_met":{"type":"string"},"enrollment_open":{"type":"string"},"flag_for_review":{"type":"string"}},"required":["seats","seats_used","minimum_threshold","threshold_met","enrollment_open","flag_for_review"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/admin":{"post":{"operationId":"corporateAccount.provisionAdmin","summary":"POST /corporate-accounts/{account}/admin \u2014 provision/reset the HR-admin portal login (RFP 24 #5)","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","maxLength":190},"password":{"type":"string","minLength":8},"name":{"type":["string","null"],"maxLength":160}},"required":["email","password"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"}},"required":["id","email"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/roster":{"get":{"operationId":"corporateAccount.roster","summary":"GET /corporate-accounts/{account}/roster \u2014 the company's employee roster (staff view)","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"employees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":["string","null"]},"employee_id":{"type":["string","null"]},"status":{"type":"string"},"plan":{"type":["string","null"]},"joined_at":{"type":"string"},"department_id":{"type":["string","null"]},"department_name":{"type":"string"}},"required":["id","name","email","employee_id","status","plan","joined_at","department_id","department_name"]}}},"required":["employees"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/invoices":{"get":{"operationId":"corporateAccount.invoices","summary":"GET /corporate-accounts/{account}/invoices \u2014 the account's B2B invoices (staff view)","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"amount_minor":{"type":"integer"},"status":{"type":"string"},"paid_minor":{"type":"integer"},"balance_minor":{"type":"integer"},"issued_at":{"type":"string"},"due_at":{"type":"string"},"paid_at":{"type":"string"}},"required":["id","number","amount_minor","status","paid_minor","balance_minor","issued_at","due_at","paid_at"]}}},"required":["invoices"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"corporateAccount.issueInvoice","summary":"POST /corporate-accounts/{account}/invoices \u2014 issue a monthly B2B invoice to the company","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"period_start":{"type":"string","format":"date-time"},"period_end":{"type":"string","format":"date-time"},"amount_minor":{"type":"integer","minimum":0}},"required":["period_start","period_end","amount_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"}},"required":["id","number"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/bill-run":{"post":{"operationId":"corporateAccount.billRun","summary":"POST /corporate-accounts/{account}/bill-run \u2014 roll this account's unbilled employer-share charges\n into an invoice now (the on-demand version of the monthly billing run)","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"amount_minor":{"type":"integer"}},"required":["id","number","amount_minor"]}}}},"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"nothing_to_bill"}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/statement":{"get":{"operationId":"corporateAccount.statement","summary":"GET /corporate-accounts/{account}/statement \u2014 AR: aging, outstanding, credit standing + line items","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"string"},"company_name":{"type":"string"}},"required":["id","company_name"]},"aging":{"type":"object","properties":{"current":{"type":"integer"},"d1_30":{"type":"integer"},"d31_60":{"type":"integer"},"d61_90":{"type":"integer"},"d90_plus":{"type":"integer"},"total":{"type":"string"}},"required":["current","d1_30","d31_60","d61_90","d90_plus","total"]},"outstanding_minor":{"type":"integer"},"overdue_minor":{"type":"integer"},"unbilled_minor":{"type":"string"},"credit_limit_minor":{"type":"integer"},"available_credit_minor":{"type":["integer","null"]},"on_credit_hold":{"type":"boolean"},"lines":{"type":"array","items":{"type":"string"}}},"required":["account","aging","outstanding_minor","overdue_minor","unbilled_minor","credit_limit_minor","available_credit_minor","on_credit_hold","lines"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/statement/pdf":{"get":{"operationId":"corporateAccount.statementPdf","summary":"GET /corporate-accounts/{account}/statement/pdf \u2014 downloadable statement of account","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/pdf":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/invoices/{invoice}/pay":{"post":{"operationId":"corporateAccount.recordPayment","summary":"POST /corporate-accounts/{account}/invoices/{invoice}/pay \u2014 staff record an offline payment","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}},{"name":"invoice","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer","minimum":1},"method":{"type":"string","enum":["bank_transfer","cash","cheque","adjustment"]},"reference":{"type":["string","null"],"maxLength":120},"paid_on":{"type":["string","null"],"format":"date-time"}},"required":["amount_minor","method"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"invoice_status":{"type":"string"},"balance_minor":{"type":"integer"}},"required":["id","invoice_status","balance_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/invoices/{invoice}/pay-online":{"post":{"operationId":"corporateAccount.payOnline","summary":"POST /corporate-accounts/{account}/invoices/{invoice}/pay-online \u2014 start a gateway payment (staff)","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}},{"name":"invoice","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"redirect_url":{"type":["string","null"]}},"required":["payment_id","redirect_url"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/departments":{"get":{"operationId":"corporateAccount.departments","summary":"GET /corporate-accounts/{account}/departments \u2014 the account's cost-centers","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"departments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"code":{"type":["string","null"]},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","code","is_active"]}}},"required":["departments"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"corporateAccount.storeDepartment","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/departments/{department}":{"patch":{"operationId":"corporateAccount.updateDepartment","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}},{"name":"department","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"corporateAccount.destroyDepartment","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}},{"name":"department","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/employees/{employee}":{"patch":{"operationId":"corporateAccount.assignDepartment","summary":"PATCH /corporate-accounts/{account}/employees/{employee} \u2014 assign an employee to a department","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}},{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"department_id":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/spend":{"get":{"operationId":"corporateAccount.spend","summary":"GET /corporate-accounts/{account}/spend \u2014 per-department headcount + employer spend","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"departments":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"code":{"type":"string"},"headcount":{"type":"integer"},"spend_minor":{"type":"integer"}},"required":["id","name","name_ar","code","headcount","spend_minor"]},{"type":"object","properties":{"id":{"type":"null"},"name":{"type":"string","const":"Unassigned"},"name_ar":{"type":"string","const":"\u063a\u064a\u0631 \u0645\u064f\u0635\u0646\u0651\u0641"},"code":{"type":"null"},"headcount":{"type":"integer"},"spend_minor":{"type":"integer"}},"required":["id","name","name_ar","code","headcount","spend_minor"]}]}},"total_spend_minor":{"anyOf":[{"type":"integer"},{"type":"number"}]},"total_headcount":{"anyOf":[{"type":"integer"},{"type":"number"}]}},"required":["departments","total_spend_minor","total_headcount"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-leads":{"get":{"operationId":"corporateLead.index","summary":"GET /corporate-leads?status= \u2014 the enquiry pipeline (newest first)","tags":["Corporate B2B"],"parameters":[{"name":"status","in":"query","schema":{"type":["string","null"],"enum":["new","contacted","converted","declined"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"leads":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"company_name":{"type":"string"},"company_name_ar":{"type":["string","null"]},"cr_number":{"type":["string","null"]},"contact_name":{"type":"string"},"contact_email":{"type":"string"},"contact_phone":{"type":["string","null"]},"est_headcount":{"type":["integer","null"]},"message":{"type":["string","null"]},"status":{"type":"string"},"converted_account_id":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","company_name","company_name_ar","cr_number","contact_name","contact_email","contact_phone","est_headcount","message","status","converted_account_id","created_at"]}},"counts":{"type":"object","properties":{"new":{"type":"integer","minimum":0},"contacted":{"type":"integer","minimum":0},"converted":{"type":"integer","minimum":0},"declined":{"type":"integer","minimum":0}},"required":["new","contacted","converted","declined"]}},"required":["leads","counts"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-leads/{lead}":{"patch":{"operationId":"corporateLead.updateStatus","summary":"PATCH /corporate-leads/{lead} \u2014 move a prospect along the pipeline (contacted / declined)","tags":["Corporate B2B"],"parameters":[{"name":"lead","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["new","contacted","declined"]}},"required":["status"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"company_name":{"type":"string"},"company_name_ar":{"type":["string","null"]},"cr_number":{"type":["string","null"]},"contact_name":{"type":"string"},"contact_email":{"type":"string"},"contact_phone":{"type":["string","null"]},"est_headcount":{"type":["integer","null"]},"message":{"type":["string","null"]},"status":{"type":"string"},"converted_account_id":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","company_name","company_name_ar","cr_number","contact_name","contact_email","contact_phone","est_headcount","message","status","converted_account_id","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-leads/{lead}/convert":{"post":{"operationId":"corporateLead.convert","summary":"POST /corporate-leads/{lead}/convert \u2014 turn a qualified prospect into a corporate account","tags":["Corporate B2B"],"parameters":[{"name":"lead","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"membership_plan_id":{"type":"string"},"seats":{"type":"integer","minimum":1},"discount_percent":{"type":"integer","minimum":0,"maximum":100}},"required":["membership_plan_id","seats"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"account_id":{"type":"string"},"lead":{"type":"object","properties":{"id":{"type":"string"},"company_name":{"type":"string"},"company_name_ar":{"type":["string","null"]},"cr_number":{"type":["string","null"]},"contact_name":{"type":"string"},"contact_email":{"type":"string"},"contact_phone":{"type":["string","null"]},"est_headcount":{"type":["integer","null"]},"message":{"type":["string","null"]},"status":{"type":"string"},"converted_account_id":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","company_name","company_name_ar","cr_number","contact_name","contact_email","contact_phone","est_headcount","message","status","converted_account_id","created_at"]}},"required":["account_id","lead"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee-benefits":{"get":{"operationId":"employeeBenefit.index","summary":"List all of the brand's employee benefits","tags":["Corporate B2B"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"employee_benefits":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"employee_id":{"type":"string"},"membership_plan_id":{"type":"string"},"benefit_type":{"type":"string"},"discount_percent":{"type":["integer","null"]},"is_active":{"type":"boolean"}},"required":["id","employee_id","membership_plan_id","benefit_type","discount_percent","is_active"]}}},"required":["employee_benefits"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"employeeBenefit.store","tags":["Corporate B2B"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employee_id":{"type":"string"},"membership_plan_id":{"type":"string"},"benefit_type":{"$ref":"#/components/schemas/BenefitType"},"discount_percent":{"type":["integer","null"],"minimum":1,"maximum":100},"is_active":{"type":"boolean"}},"required":["employee_id","membership_plan_id","benefit_type"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"employee_id":{"type":"string"},"membership_plan_id":{"type":"string"},"benefit_type":{"type":"string"},"discount_percent":{"type":["integer","null"]},"is_active":{"type":"boolean"}},"required":["id","employee_id","membership_plan_id","benefit_type","discount_percent","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee-benefits/{benefit}":{"patch":{"operationId":"employeeBenefit.update","summary":"Edit or toggle a benefit","tags":["Corporate B2B"],"parameters":[{"name":"benefit","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"membership_plan_id":{"type":"string"},"benefit_type":{"$ref":"#/components/schemas/BenefitType"},"discount_percent":{"type":["integer","null"],"minimum":1,"maximum":100},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"employee_id":{"type":"string"},"membership_plan_id":{"type":"string"},"benefit_type":{"type":"string"},"discount_percent":{"type":["integer","null"]},"is_active":{"type":"boolean"}},"required":["id","employee_id","membership_plan_id","benefit_type","discount_percent","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/corporate-accounts/{account}/members":{"get":{"operationId":"corporateAccount.members","summary":"List the members assigned to a corporate account","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"corporate_account_id":{"type":"string"},"member_id":{"type":"string"},"joined_at":{"type":"string"}},"required":["id","corporate_account_id","member_id","joined_at"]}}},"required":["members"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"corporateAccount.assignMember","summary":"Assign a member to one of the account's seats (422 when seats are exhausted)","tags":["Corporate B2B"],"parameters":[{"name":"account","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"}},"required":["member_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"corporate_account_id":{"type":"string"},"member_id":{"type":"string"},"joined_at":{"type":"string"}},"required":["id","corporate_account_id","member_id","joined_at"]}}}},"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"corporate_account_id":{"type":"string"},"member_id":{"type":"string"},"joined_at":{"type":"string"}},"required":["id","corporate_account_id","member_id","joined_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/clubs":{"get":{"operationId":"catalogue.clubs","summary":"The current brand's active clubs","tags":["Facilities & Access"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"city":{"type":["string","null"]},"gender_designation":{"type":"string"}},"required":["id","code","name","name_ar","city","gender_designation"]}}},"required":["clubs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/gate":{"post":{"operationId":"gate.decide","summary":"Decide a gate presentation (gender hard-block enforced server-side)","tags":["Facilities & Access"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"credential_identifier":{"type":"string"},"qr_token":{"type":"string"},"club_id":{"type":"string"},"area_id":{"type":["string","null"],"description":"Optional zone being entered (RFP 09 #3/#11) \u2014 re-checked server-side."}},"required":["club_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"granted":{"type":"boolean"},"reason":{"type":"string"}},"required":["granted","reason"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/gate/options":{"get":{"operationId":"gate.options","summary":"Pickers for the access-gate simulator (RFP 09/10): the brand's members (with their active\ncredential identifier + gender + age), its clubs, and its zones (with gender/min-age) \u2014 so staff\ncan present any member at any club/zone and watch the hard-blocks decide","tags":["Facilities & Access"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":["object","null"],"properties":{"credential_identifier":{"type":"string"},"member_name":{"type":"string"},"gender":{"type":"string"},"age":{"type":"string"}},"required":["credential_identifier","member_name","gender","age"]}},"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"gender_designation":{"type":"string"},"min_age":{"type":["integer","null"]}},"required":["id","name","gender_designation","min_age"]}},"zones":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"name":{"type":"string"},"gender_designation":{"type":"string"},"min_age":{"type":["integer","null"]}},"required":["id","club_id","name","gender_designation","min_age"]}}},"required":["members","clubs","zones"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/access-templates":{"get":{"operationId":"accessTemplate.index","summary":"List the brand's access templates (active and inactive)","tags":["Facilities & Access"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"access_templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"all_clubs":{"type":"boolean"},"club_ids":{"type":["array","null"],"items":{}},"time_windows":{"type":["array","null"],"items":{}},"zones":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","all_clubs","club_ids","time_windows","zones","is_active"]}}},"required":["access_templates"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"accessTemplate.store","tags":["Facilities & Access"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"all_clubs":{"type":"boolean","description":"#17 gym/club scope."},"is_active":{"type":"boolean"},"club_ids":{"type":["array","null"],"items":{"type":"string"}},"zones":{"type":["array","null"],"description":"#58 zones/areas.","items":{"type":"string"}},"time_windows":{"type":["array","null"],"description":"#18 time windows: [{\"day\":\"sun\",\"from\":\"06:00\",\"to\":\"23:00\"}, ...].","items":{"type":"object","properties":{"day":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"}}}}},"required":["name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"all_clubs":{"type":"boolean"},"club_ids":{"type":["array","null"],"items":{}},"time_windows":{"type":["array","null"],"items":{}},"zones":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","all_clubs","club_ids","time_windows","zones","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/access-templates/{template}":{"patch":{"operationId":"accessTemplate.update","tags":["Facilities & Access"],"parameters":[{"name":"template","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"all_clubs":{"type":"boolean"},"club_ids":{"type":["array","null"],"items":{}},"time_windows":{"type":["array","null"],"items":{}},"zones":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","all_clubs","club_ids","time_windows","zones","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/clubs":{"get":{"operationId":"club.index","summary":"List the brand's clubs with a zone (Area) count","tags":["Facilities & Access"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"city":{"type":["string","null"]},"region":{"type":["string","null"]},"country":{"type":["string","null"]},"tier":{"type":["string","null"]},"member_capacity":{"type":["integer","null"]},"address":{"type":["string","null"]},"gender_designation":{"type":"string"},"latitude":{"type":["number","null"]},"longitude":{"type":["number","null"]},"is_active":{"type":"boolean"},"zones_count":{"type":"integer"}},"required":["id","code","name","name_ar","city","region","country","tier","member_capacity","address","gender_designation","latitude","longitude","is_active","zones_count"]}}},"required":["clubs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"club.store","summary":"Create a club (brand_id auto-filled)","tags":["Facilities & Access"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":64},"name":{"type":"string","maxLength":120},"name_ar":{"type":"string","maxLength":120},"city":{"type":"string","maxLength":120},"region":{"type":"string","maxLength":120},"country":{"type":"string","maxLength":120},"tier":{"type":"string","maxLength":120},"member_capacity":{"type":["integer","null"],"description":"RFP 05 #5 \u2014 the club's active-membership cap (null/0 = uncapped), intentionally optional.","minimum":0},"gender_designation":{"type":"string","enum":["mixed","male","female"]},"address":{"type":"string","maxLength":255},"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180},"is_active":{"type":"boolean"}},"required":["code","name","name_ar","city","region","country","tier","gender_designation","address","latitude","longitude"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"city":{"type":["string","null"]},"region":{"type":["string","null"]},"country":{"type":["string","null"]},"tier":{"type":["string","null"]},"member_capacity":{"type":["integer","null"]},"address":{"type":["string","null"]},"gender_designation":{"type":"string"},"latitude":{"type":["number","null"]},"longitude":{"type":["number","null"]},"is_active":{"type":"boolean"},"zones_count":{"type":"integer"}},"required":["id","code","name","name_ar","city","region","country","tier","member_capacity","address","gender_designation","latitude","longitude","is_active","zones_count"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/clubs/{club}":{"patch":{"operationId":"club.update","summary":"Update a club's mutable fields. Resolved within the brand scope","tags":["Facilities & Access"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":64},"name":{"type":"string","maxLength":120},"name_ar":{"type":"string","maxLength":120},"city":{"type":"string","maxLength":120},"region":{"type":"string","maxLength":120},"country":{"type":"string","maxLength":120},"tier":{"type":"string","maxLength":120},"member_capacity":{"type":["integer","null"],"description":"RFP 05 #5 \u2014 the club's active-membership cap (null/0 = uncapped), intentionally optional.","minimum":0},"gender_designation":{"type":"string","enum":["mixed","male","female"]},"address":{"type":"string","maxLength":255},"latitude":{"type":"number","minimum":-90,"maximum":90},"longitude":{"type":"number","minimum":-180,"maximum":180},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"city":{"type":["string","null"]},"region":{"type":["string","null"]},"country":{"type":["string","null"]},"tier":{"type":["string","null"]},"member_capacity":{"type":["integer","null"]},"address":{"type":["string","null"]},"gender_designation":{"type":"string"},"latitude":{"type":["number","null"]},"longitude":{"type":["number","null"]},"is_active":{"type":"boolean"},"zones_count":{"type":"integer"}},"required":["id","code","name","name_ar","city","region","country","tier","member_capacity","address","gender_designation","latitude","longitude","is_active","zones_count"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/zones":{"get":{"operationId":"zone.index","summary":"List the brand's zones, optionally filtered to one club, ordered by name","tags":["Facilities & Access"],"parameters":[{"name":"club_id","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"zones":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"code":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"type":{"type":"string"},"gender_designation":{"type":"string"},"capacity":{"type":["integer","null"]},"is_bookable":{"type":"boolean"},"close_time":{"type":"string"},"photos":{"type":["array","null"],"items":{}},"equipment":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","club_id","code","name","name_ar","type","gender_designation","capacity","is_bookable","close_time","photos","equipment","is_active"]}}},"required":["zones"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"zone.store","summary":"Create a zone under a club in this brand (brand_id auto-filled)","tags":["Facilities & Access"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"type":{"type":"string","maxLength":64},"gender_designation":{"type":"string","enum":["mixed","male","female"]},"capacity":{"type":["integer","null"],"minimum":0},"is_bookable":{"type":"boolean"},"close_time":{"type":["string","null"]},"equipment":{"type":["array","null"],"items":{"type":"string"}},"is_active":{"type":"boolean"},"photos":{"type":["array","null"],"items":{"type":"string"}}},"required":["club_id","name","gender_designation"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"code":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"type":{"type":"string"},"gender_designation":{"type":"string"},"capacity":{"type":["integer","null"]},"is_bookable":{"type":"boolean"},"close_time":{"type":"string"},"photos":{"type":["array","null"],"items":{}},"equipment":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","club_id","code","name","name_ar","type","gender_designation","capacity","is_bookable","close_time","photos","equipment","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/zones/{zone}":{"patch":{"operationId":"zone.update","summary":"Update a zone's mutable fields. Resolved within the brand scope","tags":["Facilities & Access"],"parameters":[{"name":"zone","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"type":{"type":"string","maxLength":64},"gender_designation":{"type":"string","enum":["mixed","male","female"]},"capacity":{"type":["integer","null"],"minimum":0},"is_bookable":{"type":"boolean"},"close_time":{"type":["string","null"]},"equipment":{"type":["array","null"],"items":{"type":"string"}},"is_active":{"type":"boolean"},"photos":{"type":["array","null"],"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"code":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"type":{"type":"string"},"gender_designation":{"type":"string"},"capacity":{"type":["integer","null"]},"is_bookable":{"type":"boolean"},"close_time":{"type":"string"},"photos":{"type":["array","null"],"items":{}},"equipment":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","club_id","code","name","name_ar","type","gender_designation","capacity","is_bookable","close_time","photos","equipment","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"zone.destroy","summary":"Delete a zone","tags":["Facilities & Access"],"parameters":[{"name":"zone","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/workout-media/{item}":{"get":{"operationId":"workoutMedia.show","tags":["Fitness & Workouts"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}},"headers":{"Cache-Control":{"schema":{"type":"string","const":"public, max-age=604800, immutable"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/manage/class-workouts":{"get":{"operationId":"classWorkout.index","summary":"GET /manage/class-workouts?event_concept_id= \u2014 the workout library (optionally by class type)","tags":["Fitness & Workouts"],"parameters":[{"name":"event_concept_id","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"workouts":{"type":"array","items":{"$ref":"#/components/schemas/ClassWorkout"}}},"required":["workouts"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"classWorkout.save","summary":"POST /manage/class-workouts \u2014 create or fully replace a workout and its section/exercise/level tree.\nA day's board is small, so save does a clean full replace of the nested rows in one transaction","tags":["Fitness & Workouts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"event_concept_id":{"type":["string","null"]},"title":{"type":"string","maxLength":160},"title_ar":{"type":["string","null"],"maxLength":160},"notes":{"type":["string","null"]},"notes_ar":{"type":["string","null"]},"is_published":{"type":["boolean","null"]},"sections":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":80},"title_ar":{"type":["string","null"],"maxLength":80},"scheme":{"type":["string","null"],"enum":["amrap","for_time","rounds_for_time","emom","sets","none"]},"rounds":{"type":["integer","null"],"minimum":1},"window_sec":{"type":["integer","null"],"minimum":1},"time_cap_sec":{"type":["integer","null"],"minimum":1},"note":{"type":["string","null"],"maxLength":255},"exercises":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"fitness_exercise_id":{"type":["string","null"]},"reps":{"type":["integer","null"],"minimum":0},"reps_text":{"type":["string","null"],"maxLength":40},"duration_sec":{"type":["integer","null"],"minimum":0},"load_type":{"type":["string","null"],"enum":["absolute","percent_1rm","rpe","bodyweight","none"]},"load_value":{"type":["number","null"]},"load_value_max":{"type":["number","null"]},"load_unit":{"type":["string","null"],"enum":["kg","lb","percent","rpe"]},"sets":{"type":["array","null"],"items":{"type":"string"}},"note":{"type":["string","null"],"maxLength":160},"levels":{"type":"array","items":{"type":"object","properties":{"level_id":{"type":"string"},"load_value":{"type":["number","null"]},"load_unit":{"type":["string","null"],"enum":["kg","lb"]},"reps":{"type":["integer","null"],"minimum":0},"note":{"type":["string","null"],"maxLength":160}},"required":["level_id"]}}},"required":["name"]}}},"required":["title"]}}},"required":["title"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"notes":{"type":["string","null"]},"notes_ar":{"type":["string","null"]},"member_level":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]},"sections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"scheme":{"type":"string"},"rounds":{"type":["integer","null"]},"window_sec":{"type":["integer","null"]},"time_cap_sec":{"type":["integer","null"]},"note":{"type":["string","null"]},"note_ar":{"type":["string","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"fitness_exercise_id":{"type":["string","null"]},"reps":{"type":["integer","null"]},"reps_text":{"type":["string","null"]},"duration_sec":{"type":["integer","null"]},"load_type":{"type":"string"},"load_value":{"type":["number","null"]},"load_value_max":{"type":["number","null"]},"load_unit":{"type":["string","null"]},"sets":{"type":["array","null"],"items":{}},"note":{"type":["string","null"]},"note_ar":{"type":["string","null"]},"levels":{"type":"array","description":"Every level's prescription (staff builder / \"other levels\" chip).","items":{"type":"object","properties":{"level_id":{"type":"string"},"load_value":{"type":["number","null"]},"load_unit":{"type":["string","null"]},"reps":{"type":["integer","null"]},"note":{"type":["string","null"]}},"required":["level_id","load_value","load_unit","reps","note"]}},"member_load":{"description":"The one prescription THIS member should read.","anyOf":[{"type":"object","properties":{"display":{"type":"null"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"none"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string","const":"BW"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"bodyweight"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"rpe"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"anyOf":[{"type":"string"},{"type":"string","enum":["% 1RM"]}]},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"percent_only"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"number"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"one_rm"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"number"},"reps":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"source":{"type":"string","enum":["level","base"]}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"null"},"weight_kg":{"type":"null"},"reps":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"source":{"type":"string","const":"none"}},"required":["display","weight_kg","reps","source"]},{"type":"null"}]}},"required":["id","name","name_ar","fitness_exercise_id","reps","reps_text","duration_sec","load_type","load_value","load_value_max","load_unit","sets","note","note_ar","levels","member_load"]}}},"required":["id","title","title_ar","scheme","rounds","window_sec","time_cap_sec","note","note_ar","exercises"]}}},"required":["id","title","title_ar","notes","notes_ar","member_level","sections"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/class-workouts/{workout}":{"get":{"operationId":"classWorkout.show","summary":"GET /manage/class-workouts/{workout} \u2014 the full tree (all levels), for the builder","tags":["Fitness & Workouts"],"parameters":[{"name":"workout","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"notes":{"type":["string","null"]},"notes_ar":{"type":["string","null"]},"member_level":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]},"sections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"scheme":{"type":"string"},"rounds":{"type":["integer","null"]},"window_sec":{"type":["integer","null"]},"time_cap_sec":{"type":["integer","null"]},"note":{"type":["string","null"]},"note_ar":{"type":["string","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"fitness_exercise_id":{"type":["string","null"]},"reps":{"type":["integer","null"]},"reps_text":{"type":["string","null"]},"duration_sec":{"type":["integer","null"]},"load_type":{"type":"string"},"load_value":{"type":["number","null"]},"load_value_max":{"type":["number","null"]},"load_unit":{"type":["string","null"]},"sets":{"type":["array","null"],"items":{}},"note":{"type":["string","null"]},"note_ar":{"type":["string","null"]},"levels":{"type":"array","description":"Every level's prescription (staff builder / \"other levels\" chip).","items":{"type":"object","properties":{"level_id":{"type":"string"},"load_value":{"type":["number","null"]},"load_unit":{"type":["string","null"]},"reps":{"type":["integer","null"]},"note":{"type":["string","null"]}},"required":["level_id","load_value","load_unit","reps","note"]}},"member_load":{"description":"The one prescription THIS member should read.","anyOf":[{"type":"object","properties":{"display":{"type":"null"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"none"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string","const":"BW"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"bodyweight"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"rpe"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"anyOf":[{"type":"string"},{"type":"string","enum":["% 1RM"]}]},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"percent_only"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"number"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"one_rm"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"number"},"reps":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"source":{"type":"string","enum":["level","base"]}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"null"},"weight_kg":{"type":"null"},"reps":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"source":{"type":"string","const":"none"}},"required":["display","weight_kg","reps","source"]},{"type":"null"}]}},"required":["id","name","name_ar","fitness_exercise_id","reps","reps_text","duration_sec","load_type","load_value","load_value_max","load_unit","sets","note","note_ar","levels","member_load"]}}},"required":["id","title","title_ar","scheme","rounds","window_sec","time_cap_sec","note","note_ar","exercises"]}}},"required":["id","title","title_ar","notes","notes_ar","member_level","sections"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"classWorkout.destroy","summary":"DELETE /manage/class-workouts/{workout} \u2014 remove a workout and its tree; detach any links","tags":["Fitness & Workouts"],"parameters":[{"name":"workout","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/class-workouts/{workout}/attach":{"post":{"operationId":"classWorkout.attach","summary":"POST /manage/class-workouts/{workout}/attach \u2014 set as a class-type default or a session override","tags":["Fitness & Workouts"],"parameters":[{"name":"workout","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"target":{"type":"string","enum":["type","session"]},"event_concept_id":{"type":["string","null"]},"session_id":{"type":["string","null"]}},"required":["target"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-providers":{"get":{"operationId":"workoutContentAdmin.index","summary":"GET /manage/workout-providers \u2014 brand configs + the platform's available drivers","tags":["Fitness & Workouts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"available":{"type":"array","prefixItems":[{"type":"string","const":"workoutx"},{"type":"string","const":"sugarwod"}],"minItems":2,"maxItems":2,"additionalItems":false},"providers":{"type":"array","description":"Only the current offering is listed \u2014 configs for paused providers stay in the\nDB (and would resurface if their key returns to the offering) but don't clutter\nthe page while they're not sellable.","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"display_name":{"type":"string"},"display_name_ar":{"type":["string","null"]},"has_credentials":{"type":"string"},"settings":{"type":["array","null"],"items":{}},"is_enabled":{"type":"boolean"},"sort":{"type":"integer"},"item_count":{"type":"integer","minimum":0},"hub_count":{"type":"integer","minimum":0},"last_synced_at":{}},"required":["id","provider","display_name","display_name_ar","has_credentials","settings","is_enabled","sort","item_count","hub_count","last_synced_at"]}}},"required":["available","providers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"workoutContentAdmin.store","summary":"POST /manage/workout-providers \u2014 link a provider to the brand","tags":["Fitness & Workouts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","enum":["workoutx","sugarwod"]},"display_name":{"type":"string","maxLength":120},"display_name_ar":{"type":["string","null"],"maxLength":120},"credentials":{"type":["array","null"],"items":{"type":"string"}},"settings":{"type":["array","null"],"items":{"type":"string"}},"is_enabled":{"type":"boolean"},"sort":{"type":"integer","minimum":0,"maximum":999}},"required":["provider","display_name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"display_name":{"type":"string"},"display_name_ar":{"type":["string","null"]},"has_credentials":{"type":"string"},"settings":{"type":["array","null"],"items":{}},"is_enabled":{"type":"boolean"},"sort":{"type":"integer"},"item_count":{"type":"integer","minimum":0},"hub_count":{"type":"integer","minimum":0},"last_synced_at":{}},"required":["id","provider","display_name","display_name_ar","has_credentials","settings","is_enabled","sort","item_count","hub_count","last_synced_at"]}},"required":["provider"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-providers/{config}":{"patch":{"operationId":"workoutContentAdmin.update","summary":"PATCH /manage/workout-providers/{config}","tags":["Fitness & Workouts"],"parameters":[{"name":"config","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"display_name":{"type":"string","maxLength":120},"display_name_ar":{"type":["string","null"],"maxLength":120},"credentials":{"type":["array","null"],"items":{"type":"string"}},"settings":{"type":["array","null"],"items":{"type":"string"}},"is_enabled":{"type":"boolean"},"sort":{"type":"integer","minimum":0,"maximum":999}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"display_name":{"type":"string"},"display_name_ar":{"type":["string","null"]},"has_credentials":{"type":"string"},"settings":{"type":["array","null"],"items":{}},"is_enabled":{"type":"boolean"},"sort":{"type":"integer"},"item_count":{"type":"integer","minimum":0},"hub_count":{"type":"integer","minimum":0},"last_synced_at":{}},"required":["id","provider","display_name","display_name_ar","has_credentials","settings","is_enabled","sort","item_count","hub_count","last_synced_at"]}},"required":["provider"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-providers/{config}/test":{"post":{"operationId":"workoutContentAdmin.test","summary":"POST /manage/workout-providers/{config}/test \u2014 driver healthCheck","tags":["Fitness & Workouts"],"parameters":[{"name":"config","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-providers/{config}/sync":{"post":{"operationId":"workoutContentAdmin.sync","summary":"POST /manage/workout-providers/{config}/sync \u2014 refresh the catalogue snapshot","tags":["Fitness & Workouts"],"parameters":[{"name":"config","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"synced":{"type":"integer"}},"required":["synced"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-groups":{"get":{"operationId":"workoutGroupAdmin.index","summary":"GET /manage/workout-groups","tags":["Fitness & Workouts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"cover_url":{"type":["string","null"]},"type":{"type":"string"},"is_active":{"type":"boolean"},"is_featured":{"type":"boolean"},"sort":{"type":"integer"},"item_count":{"type":"integer"}},"required":["id","name","name_ar","description","description_ar","cover_url","type","is_active","is_featured","sort","item_count"]}}},"required":["groups"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"workoutGroupAdmin.store","summary":"POST /manage/workout-groups","tags":["Fitness & Workouts"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"group":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"cover_url":{"type":["string","null"]},"type":{"type":"string"},"is_active":{"type":"boolean"},"is_featured":{"type":"boolean"},"sort":{"type":"integer"},"item_count":{"type":"integer"}},"required":["id","name","name_ar","description","description_ar","cover_url","type","is_active","is_featured","sort","item_count"]}},"required":["group"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-groups/{group}":{"patch":{"operationId":"workoutGroupAdmin.update","summary":"PATCH /manage/workout-groups/{group}","tags":["Fitness & Workouts"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"group":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"cover_url":{"type":["string","null"]},"type":{"type":"string"},"is_active":{"type":"boolean"},"is_featured":{"type":"boolean"},"sort":{"type":"integer"},"item_count":{"type":"integer"}},"required":["id","name","name_ar","description","description_ar","cover_url","type","is_active","is_featured","sort","item_count"]}},"required":["group"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"workoutGroupAdmin.destroy","summary":"DELETE /manage/workout-groups/{group} \u2014 memberships cascade; items fall back to source","tags":["Fitness & Workouts"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-items/{item}/groups":{"put":{"operationId":"workoutGroupAdmin.syncItemGroups","summary":"PUT /manage/workout-items/{item}/groups {group_ids: []} \u2014 the picker's confirm:\nsync THIS item's memberships to exactly the given groups","tags":["Fitness & Workouts"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"group_ids":{"type":"array","items":{"type":"string"}}},"required":["group_ids"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"item_id":{"type":"string"},"groups":{"type":"string"},"in_hub":{"type":"boolean"}},"required":["item_id","groups","in_hub"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-items":{"get":{"operationId":"workoutContentAdmin.items","summary":"GET /manage/workout-items?provider=&q=&hidden= \u2014 the curatable library","tags":["Fitness & Workouts"],"parameters":[{"name":"provider","in":"query","schema":{"type":["string","null"]}},{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":120}},{"name":"hub","in":"query","description":"hub=1 \u2192 the brand's published hub; hub=0 \u2192 the source pool still to pick from.","schema":{"type":["boolean","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"thumbnail_url":{"description":"Licensed assets go through the media proxy (the provider key never leaves the server).","anyOf":[{"type":"string"},{"type":"null"}]},"duration_seconds":{"type":["integer","null"]},"level":{"type":["string","null"]},"category":{"type":["string","null"]},"instructor":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"equipment":{"type":"array","items":{}},"media_url":{"type":["string","null"]},"playback":{"type":["object","null"],"description":"Server-resolved playback envelope \u2014 the staff preview plays exactly what\nmembers will (youtube embed for video items, none for inline GIFs).","properties":{"kind":{"type":"string"},"url":{"type":["string","null"]},"expires_at":{"type":["string","null"]}},"required":["kind","url","expires_at"]},"is_premium":{"type":"boolean"},"is_featured":{"type":"boolean"},"is_hidden":{"type":"boolean"},"in_hub":{"type":"boolean"},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]}},"sort":{"type":"integer"},"synced_at":{"type":"string"}},"required":["id","provider","type","title","title_ar","thumbnail_url","duration_seconds","level","category","instructor","description","description_ar","category_ar","equipment","media_url","playback","is_premium","is_featured","is_hidden","in_hub","groups","sort","synced_at"]}}},"required":["items"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"workoutContentAdmin.storeItem","summary":"POST /manage/workout-items \u2014 author an item. YouTube: paste a URL, metadata comes\nfrom keyless oEmbed. Internal: full manual metadata + a platform-storage video URL","tags":["Fitness & Workouts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"string","enum":["internal","youtube"]},"url":{"type":["string","null"],"maxLength":500},"title":{"type":["string","null"],"maxLength":200},"title_ar":{"type":["string","null"],"maxLength":200},"description":{"type":["string","null"],"maxLength":2000},"media_url":{"type":["string","null"],"maxLength":500},"thumbnail_url":{"type":["string","null"],"maxLength":500},"duration_seconds":{"type":["integer","null"],"minimum":1},"level":{"type":["string","null"],"enum":["beginner","intermediate","advanced"]},"category":{"type":["string","null"],"maxLength":60},"instructor":{"type":["string","null"],"maxLength":120},"is_premium":{"type":"boolean"}},"required":["provider"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"thumbnail_url":{"description":"Licensed assets go through the media proxy (the provider key never leaves the server).","anyOf":[{"type":"string"},{"type":"null"}]},"duration_seconds":{"type":["integer","null"]},"level":{"type":["string","null"]},"category":{"type":["string","null"]},"instructor":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"equipment":{"type":"array","items":{}},"media_url":{"type":["string","null"]},"playback":{"type":["object","null"],"description":"Server-resolved playback envelope \u2014 the staff preview plays exactly what\nmembers will (youtube embed for video items, none for inline GIFs).","properties":{"kind":{"type":"string"},"url":{"type":["string","null"]},"expires_at":{"type":["string","null"]}},"required":["kind","url","expires_at"]},"is_premium":{"type":"boolean"},"is_featured":{"type":"boolean"},"is_hidden":{"type":"boolean"},"in_hub":{"type":"boolean"},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]}},"sort":{"type":"integer"},"synced_at":{"type":"string"}},"required":["id","provider","type","title","title_ar","thumbnail_url","duration_seconds","level","category","instructor","description","description_ar","category_ar","equipment","media_url","playback","is_premium","is_featured","is_hidden","in_hub","groups","sort","synced_at"]}},"required":["item"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-items/translate-missing":{"post":{"operationId":"workoutContentAdmin.translateMissing","summary":"POST /manage/workout-items/translate-missing \u2014 AI-fill Arabic where title_ar is blank (batch-capped)","tags":["Fitness & Workouts"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"limit":{"type":["integer","null"],"minimum":1,"maximum":50}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"translated":{"type":"integer"},"remaining":{"type":"integer","minimum":0}},"required":["translated","remaining"]}}}},"503":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"AI translation is not configured."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/club-plans":{"get":{"operationId":"clubPlanAdmin.index","summary":"GET /manage/club-plans \u2014 every plan (published + draft) with day counts and adoption","tags":["Fitness & Workouts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"total_days":{"type":["integer","null"]},"days_authored":{"type":"integer"},"featured":{"type":"boolean"},"published":{"type":"boolean"},"target_area":{"type":["string","null"]},"joined":{"type":"integer"},"active":{"type":"integer"},"updated_at":{"type":"string"}},"required":["id","title","level","cover_url","total_days","days_authored","featured","published","target_area","joined","active","updated_at"]}}},"required":["plans"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"clubPlanAdmin.store","summary":"POST /manage/club-plans \u2014 create a draft plan (metadata only; days are authored separately)","tags":["Fitness & Workouts"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"total_days":{"type":["integer","null"]},"days_authored":{"type":"integer"},"featured":{"type":"boolean"},"published":{"type":"boolean"},"target_area":{"type":["string","null"]},"joined":{"type":"integer"},"active":{"type":"integer"},"updated_at":{"type":"string"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"coach_id":{"type":"string"},"weeks":{"type":"integer"},"catalog_meta":{"type":"object","properties":{"target_area":{"type":["string","null"],"description":"Flattened for the staff editor (the member catalog keeps these bilingual)."},"equipment":{"type":"array","items":{}},"tags":{"type":"array","items":{}},"benefits":{"type":"array","items":{}},"schedule":{"type":"array","items":{}},"min_daily_min":{"type":["string","null"]},"max_daily_min":{"type":["string","null"]}},"required":["target_area","equipment","tags","benefits","schedule","min_daily_min","max_daily_min"]},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sort":{"type":"integer"},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exercise_id":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"]},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","exercise_id","title","sets","reps","duration_sec","rest_sec","target_weight_kg","note"]}}},"required":["id","name","sort","exercises"]}}},"required":["id","title","level","cover_url","total_days","days_authored","featured","published","target_area","joined","active","updated_at","goal","coach_id","weeks","catalog_meta","days"]}},"required":["plan"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/club-plans/{plan}":{"get":{"operationId":"clubPlanAdmin.show","summary":"GET /manage/club-plans/{plan} \u2014 the plan + its authored pattern days/exercises + adoption","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"total_days":{"type":["integer","null"]},"days_authored":{"type":"integer"},"featured":{"type":"boolean"},"published":{"type":"boolean"},"target_area":{"type":["string","null"]},"joined":{"type":"integer"},"active":{"type":"integer"},"updated_at":{"type":"string"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"coach_id":{"type":"string"},"weeks":{"type":"integer"},"catalog_meta":{"type":"object","properties":{"target_area":{"type":["string","null"],"description":"Flattened for the staff editor (the member catalog keeps these bilingual)."},"equipment":{"type":"array","items":{}},"tags":{"type":"array","items":{}},"benefits":{"type":"array","items":{}},"schedule":{"type":"array","items":{}},"min_daily_min":{"type":["string","null"]},"max_daily_min":{"type":["string","null"]}},"required":["target_area","equipment","tags","benefits","schedule","min_daily_min","max_daily_min"]},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sort":{"type":"integer"},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exercise_id":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"]},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","exercise_id","title","sets","reps","duration_sec","rest_sec","target_weight_kg","note"]}}},"required":["id","name","sort","exercises"]}}},"required":["id","title","level","cover_url","total_days","days_authored","featured","published","target_area","joined","active","updated_at","goal","coach_id","weeks","catalog_meta","days"]}},"required":["plan"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"clubPlanAdmin.update","summary":"PATCH /manage/club-plans/{plan} \u2014 edit metadata (title / goal / level / cover / length / catalog meta)","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"total_days":{"type":["integer","null"]},"days_authored":{"type":"integer"},"featured":{"type":"boolean"},"published":{"type":"boolean"},"target_area":{"type":["string","null"]},"joined":{"type":"integer"},"active":{"type":"integer"},"updated_at":{"type":"string"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"coach_id":{"type":"string"},"weeks":{"type":"integer"},"catalog_meta":{"type":"object","properties":{"target_area":{"type":["string","null"],"description":"Flattened for the staff editor (the member catalog keeps these bilingual)."},"equipment":{"type":"array","items":{}},"tags":{"type":"array","items":{}},"benefits":{"type":"array","items":{}},"schedule":{"type":"array","items":{}},"min_daily_min":{"type":["string","null"]},"max_daily_min":{"type":["string","null"]}},"required":["target_area","equipment","tags","benefits","schedule","min_daily_min","max_daily_min"]},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sort":{"type":"integer"},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exercise_id":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"]},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","exercise_id","title","sets","reps","duration_sec","rest_sec","target_weight_kg","note"]}}},"required":["id","name","sort","exercises"]}}},"required":["id","title","level","cover_url","total_days","days_authored","featured","published","target_area","joined","active","updated_at","goal","coach_id","weeks","catalog_meta","days"]}},"required":["plan"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"clubPlanAdmin.destroy","summary":"DELETE /manage/club-plans/{plan} \u2014 hard delete, blocked once members have joined (unpublish instead)","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"409":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Members have joined this plan \u2014 unpublish it instead of deleting."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/club-plans/{plan}/days":{"put":{"operationId":"clubPlanAdmin.days","summary":"PUT /manage/club-plans/{plan}/days \u2014 replace the plan's authored pattern days + their exercises","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"total_days":{"type":["integer","null"],"minimum":1,"maximum":366},"days":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"exercises":{"type":["array","null"],"items":{"type":"object","properties":{"exercise_id":{"type":"string"},"sets":{"type":"integer","minimum":1,"maximum":20},"reps":{"type":"integer","minimum":1,"maximum":100},"duration_sec":{"type":["integer","null"],"minimum":1,"maximum":3600},"rest_sec":{"type":"integer","minimum":0,"maximum":900},"target_weight_kg":{"type":["number","null"],"minimum":0,"maximum":1000},"note":{"type":["string","null"]}},"required":["exercise_id","sets","reps","rest_sec"]}}},"required":["name"]}}},"required":["days"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"total_days":{"type":["integer","null"]},"days_authored":{"type":"integer"},"featured":{"type":"boolean"},"published":{"type":"boolean"},"target_area":{"type":["string","null"]},"joined":{"type":"integer"},"active":{"type":"integer"},"updated_at":{"type":"string"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"coach_id":{"type":"string"},"weeks":{"type":"integer"},"catalog_meta":{"type":"object","properties":{"target_area":{"type":["string","null"],"description":"Flattened for the staff editor (the member catalog keeps these bilingual)."},"equipment":{"type":"array","items":{}},"tags":{"type":"array","items":{}},"benefits":{"type":"array","items":{}},"schedule":{"type":"array","items":{}},"min_daily_min":{"type":["string","null"]},"max_daily_min":{"type":["string","null"]}},"required":["target_area","equipment","tags","benefits","schedule","min_daily_min","max_daily_min"]},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sort":{"type":"integer"},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exercise_id":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"]},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","exercise_id","title","sets","reps","duration_sec","rest_sec","target_weight_kg","note"]}}},"required":["id","name","sort","exercises"]}}},"required":["id","title","level","cover_url","total_days","days_authored","featured","published","target_area","joined","active","updated_at","goal","coach_id","weeks","catalog_meta","days"]}},"required":["plan"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/club-plans/{plan}/publish":{"post":{"operationId":"clubPlanAdmin.publish","summary":"POST /manage/club-plans/{plan}/publish \u2014 make it visible in the member catalog","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"total_days":{"type":["integer","null"]},"days_authored":{"type":"integer"},"featured":{"type":"boolean"},"published":{"type":"boolean"},"target_area":{"type":["string","null"]},"joined":{"type":"integer"},"active":{"type":"integer"},"updated_at":{"type":"string"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"coach_id":{"type":"string"},"weeks":{"type":"integer"},"catalog_meta":{"type":"object","properties":{"target_area":{"type":["string","null"],"description":"Flattened for the staff editor (the member catalog keeps these bilingual)."},"equipment":{"type":"array","items":{}},"tags":{"type":"array","items":{}},"benefits":{"type":"array","items":{}},"schedule":{"type":"array","items":{}},"min_daily_min":{"type":["string","null"]},"max_daily_min":{"type":["string","null"]}},"required":["target_area","equipment","tags","benefits","schedule","min_daily_min","max_daily_min"]},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sort":{"type":"integer"},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exercise_id":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"]},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","exercise_id","title","sets","reps","duration_sec","rest_sec","target_weight_kg","note"]}}},"required":["id","name","sort","exercises"]}}},"required":["id","title","level","cover_url","total_days","days_authored","featured","published","target_area","joined","active","updated_at","goal","coach_id","weeks","catalog_meta","days"]}},"required":["plan"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Add at least one day before publishing."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/club-plans/{plan}/unpublish":{"post":{"operationId":"clubPlanAdmin.unpublish","summary":"POST /manage/club-plans/{plan}/unpublish \u2014 pull it from the catalog (existing joins keep working)","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"total_days":{"type":["integer","null"]},"days_authored":{"type":"integer"},"featured":{"type":"boolean"},"published":{"type":"boolean"},"target_area":{"type":["string","null"]},"joined":{"type":"integer"},"active":{"type":"integer"},"updated_at":{"type":"string"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"coach_id":{"type":"string"},"weeks":{"type":"integer"},"catalog_meta":{"type":"object","properties":{"target_area":{"type":["string","null"],"description":"Flattened for the staff editor (the member catalog keeps these bilingual)."},"equipment":{"type":"array","items":{}},"tags":{"type":"array","items":{}},"benefits":{"type":"array","items":{}},"schedule":{"type":"array","items":{}},"min_daily_min":{"type":["string","null"]},"max_daily_min":{"type":["string","null"]}},"required":["target_area","equipment","tags","benefits","schedule","min_daily_min","max_daily_min"]},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sort":{"type":"integer"},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exercise_id":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"]},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","exercise_id","title","sets","reps","duration_sec","rest_sec","target_weight_kg","note"]}}},"required":["id","name","sort","exercises"]}}},"required":["id","title","level","cover_url","total_days","days_authored","featured","published","target_area","joined","active","updated_at","goal","coach_id","weeks","catalog_meta","days"]}},"required":["plan"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/club-plans/{plan}/feature":{"post":{"operationId":"clubPlanAdmin.feature","summary":"POST /manage/club-plans/{plan}/feature {featured} \u2014 pin (or unpin) it to the top of the catalog","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"featured":{"type":"boolean"}},"required":["featured"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"total_days":{"type":["integer","null"]},"days_authored":{"type":"integer"},"featured":{"type":"boolean"},"published":{"type":"boolean"},"target_area":{"type":["string","null"]},"joined":{"type":"integer"},"active":{"type":"integer"},"updated_at":{"type":"string"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"coach_id":{"type":"string"},"weeks":{"type":"integer"},"catalog_meta":{"type":"object","properties":{"target_area":{"type":["string","null"],"description":"Flattened for the staff editor (the member catalog keeps these bilingual)."},"equipment":{"type":"array","items":{}},"tags":{"type":"array","items":{}},"benefits":{"type":"array","items":{}},"schedule":{"type":"array","items":{}},"min_daily_min":{"type":["string","null"]},"max_daily_min":{"type":["string","null"]}},"required":["target_area","equipment","tags","benefits","schedule","min_daily_min","max_daily_min"]},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sort":{"type":"integer"},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exercise_id":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"]},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","exercise_id","title","sets","reps","duration_sec","rest_sec","target_weight_kg","note"]}}},"required":["id","name","sort","exercises"]}}},"required":["id","title","level","cover_url","total_days","days_authored","featured","published","target_area","joined","active","updated_at","goal","coach_id","weeks","catalog_meta","days"]}},"required":["plan"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/club-plans/{plan}/adoption":{"get":{"operationId":"clubPlanAdmin.adoption","summary":"GET /manage/club-plans/{plan}/adoption \u2014 joins, active, completion signal for the plan","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"adoption":{"type":"object","properties":{"joined":{"type":"integer","minimum":0},"active":{"type":"integer","minimum":0},"with_progress":{"type":"integer","minimum":0},"days_completed":{"type":"integer","minimum":0}},"required":["joined","active","with_progress","days_completed"]}},"required":["adoption"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/club-plans/{plan}/members":{"get":{"operationId":"clubPlanAdmin.members","summary":"GET /manage/club-plans/{plan}/members \u2014 the roster who joined, each with day-progress + last activity","tags":["Fitness & Workouts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"member_id":{"type":["string","null"]},"name":{"type":"string"},"joined_at":{"type":"string"},"active":{"type":"boolean"},"days_completed":{"type":"integer"},"total_days":{"type":"integer"},"pct":{"type":"integer"},"last_activity_at":{"type":["string","null"]}},"required":["member_id","name","joined_at","active","days_completed","total_days","pct","last_activity_at"]}}},"required":["members"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-items/{item}/translate":{"post":{"operationId":"workoutContentAdmin.translateItem","summary":"POST /manage/workout-items/{item}/translate \u2014 AI-generate Arabic for one item","tags":["Fitness & Workouts"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"thumbnail_url":{"description":"Licensed assets go through the media proxy (the provider key never leaves the server).","anyOf":[{"type":"string"},{"type":"null"}]},"duration_seconds":{"type":["integer","null"]},"level":{"type":["string","null"]},"category":{"type":["string","null"]},"instructor":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"equipment":{"type":"array","items":{}},"media_url":{"type":["string","null"]},"playback":{"type":["object","null"],"description":"Server-resolved playback envelope \u2014 the staff preview plays exactly what\nmembers will (youtube embed for video items, none for inline GIFs).","properties":{"kind":{"type":"string"},"url":{"type":["string","null"]},"expires_at":{"type":["string","null"]}},"required":["kind","url","expires_at"]},"is_premium":{"type":"boolean"},"is_featured":{"type":"boolean"},"is_hidden":{"type":"boolean"},"in_hub":{"type":"boolean"},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]}},"sort":{"type":"integer"},"synced_at":{"type":"string"}},"required":["id","provider","type","title","title_ar","thumbnail_url","duration_seconds","level","category","instructor","description","description_ar","category_ar","equipment","media_url","playback","is_premium","is_featured","is_hidden","in_hub","groups","sort","synced_at"]}},"required":["item"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/wods":{"get":{"operationId":"workoutWodAdmin.index","summary":"GET /manage/wods?from=&to= \u2014 the programming calendar with score counts","tags":["Fitness & Workouts"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"wods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"track":{"type":"string"},"date":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"scoring_type":{"type":"string"},"movements":{"type":"array","items":{}},"sections":{"anyOf":[{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"wod"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"movements":{"type":"array","items":{}},"scored":{"type":"boolean"},"scoring_type":{"type":"string"},"leaderboard":{"type":"boolean"},"sort":{"type":"integer"}},"required":["key","title","title_ar","movements","scored","scoring_type","leaderboard","sort"]}],"minItems":1,"maxItems":1,"additionalItems":false},{"type":"array","items":{}}]},"allow_scaled":{"type":"boolean"},"is_published":{"type":"boolean"},"source":{"type":"string"},"score_count":{"type":"integer"}},"required":["id","track","date","title","title_ar","description","description_ar","scoring_type","movements","sections","allow_scaled","is_published","source","score_count"]}}},"required":["wods"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"workoutWodAdmin.store","tags":["Fitness & Workouts"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"wod":{"type":"object","properties":{"id":{"type":"string"},"track":{"type":"string"},"date":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"scoring_type":{"type":"string"},"movements":{"type":"array","items":{}},"sections":{"anyOf":[{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"wod"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"movements":{"type":"array","items":{}},"scored":{"type":"boolean"},"scoring_type":{"type":"string"},"leaderboard":{"type":"boolean"},"sort":{"type":"integer"}},"required":["key","title","title_ar","movements","scored","scoring_type","leaderboard","sort"]}],"minItems":1,"maxItems":1,"additionalItems":false},{"type":"array","items":{}}]},"allow_scaled":{"type":"boolean"},"is_published":{"type":"boolean"},"source":{"type":"string"},"score_count":{"type":"integer"}},"required":["id","track","date","title","title_ar","description","description_ar","scoring_type","movements","sections","allow_scaled","is_published","source","score_count"]}},"required":["wod"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/wods/{wod}":{"patch":{"operationId":"workoutWodAdmin.update","tags":["Fitness & Workouts"],"parameters":[{"name":"wod","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"wod":{"type":"object","properties":{"id":{"type":"string"},"track":{"type":"string"},"date":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"scoring_type":{"type":"string"},"movements":{"type":"array","items":{}},"sections":{"anyOf":[{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"wod"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"movements":{"type":"array","items":{}},"scored":{"type":"boolean"},"scoring_type":{"type":"string"},"leaderboard":{"type":"boolean"},"sort":{"type":"integer"}},"required":["key","title","title_ar","movements","scored","scoring_type","leaderboard","sort"]}],"minItems":1,"maxItems":1,"additionalItems":false},{"type":"array","items":{}}]},"allow_scaled":{"type":"boolean"},"is_published":{"type":"boolean"},"source":{"type":"string"},"score_count":{"type":"integer"}},"required":["id","track","date","title","title_ar","description","description_ar","scoring_type","movements","sections","allow_scaled","is_published","source","score_count"]}},"required":["wod"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"workoutWodAdmin.destroy","tags":["Fitness & Workouts"],"parameters":[{"name":"wod","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/wods/{wod}/publish":{"post":{"operationId":"workoutWodAdmin.publish","summary":"POST /manage/wods/{wod}/publish {published:bool}","tags":["Fitness & Workouts"],"parameters":[{"name":"wod","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"published":{"type":"boolean"}},"required":["published"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"wod":{"type":"object","properties":{"id":{"type":"string"},"track":{"type":"string"},"date":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"scoring_type":{"type":"string"},"movements":{"type":"array","items":{}},"sections":{"anyOf":[{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"wod"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"movements":{"type":"array","items":{}},"scored":{"type":"boolean"},"scoring_type":{"type":"string"},"leaderboard":{"type":"boolean"},"sort":{"type":"integer"}},"required":["key","title","title_ar","movements","scored","scoring_type","leaderboard","sort"]}],"minItems":1,"maxItems":1,"additionalItems":false},{"type":"array","items":{}}]},"allow_scaled":{"type":"boolean"},"is_published":{"type":"boolean"},"source":{"type":"string"},"score_count":{"type":"integer"}},"required":["id","track","date","title","title_ar","description","description_ar","scoring_type","movements","sections","allow_scaled","is_published","source","score_count"]}},"required":["wod"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/wods/{wod}/leaderboard":{"get":{"operationId":"workoutWodAdmin.leaderboard","summary":"GET /manage/wods/{wod}/leaderboard?scale=&gender= \u2014 the staff board view","tags":["Fitness & Workouts"],"parameters":[{"name":"wod","in":"path","required":true,"schema":{"type":"string"}},{"name":"scale","in":"query","schema":{"type":["string","null"],"enum":["rx","scaled","all"]}},{"name":"gender","in":"query","schema":{"type":["string","null"],"enum":["male","female","all"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"wod":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"scoring_type":{"type":"string"}},"required":["id","title","scoring_type"]},"weight_unit":{},"entries":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"string"},"member":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"initial":{"type":"string"},"is_me":{"type":"string"}},"required":["id","name","initial","is_me"]},"result":{"type":"string"},"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":["number","null"]},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"}},"required":["rank","member","result","scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at"]}},"total":{"type":"integer"}},"required":["wod","weight_unit","entries","total"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/benchmarks":{"get":{"operationId":"workoutBenchmarkAdmin.index","tags":["Fitness & Workouts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"benchmarks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"category":{"type":["string","null"]},"scoring_type":{"type":"string"},"is_active":{"type":"boolean"},"source":{"type":"string"},"result_count":{"type":"integer"}},"required":["id","name","name_ar","description","category","scoring_type","is_active","source","result_count"]}}},"required":["benchmarks"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"workoutBenchmarkAdmin.store","tags":["Fitness & Workouts"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"benchmark":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"category":{"type":["string","null"]},"scoring_type":{"type":"string"},"is_active":{"type":"boolean"},"source":{"type":"string"},"result_count":{"type":"integer"}},"required":["id","name","name_ar","description","category","scoring_type","is_active","source","result_count"]}},"required":["benchmark"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/benchmarks/seed":{"post":{"operationId":"workoutBenchmarkAdmin.seed","summary":"POST /manage/benchmarks/seed \u2014 insert the standard set (idempotent per brand)","tags":["Fitness & Workouts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"seeded":{"type":"integer"},"total":{"type":"integer","minimum":0}},"required":["seeded","total"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/benchmarks/{benchmark}":{"patch":{"operationId":"workoutBenchmarkAdmin.update","tags":["Fitness & Workouts"],"parameters":[{"name":"benchmark","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"benchmark":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"category":{"type":["string","null"]},"scoring_type":{"type":"string"},"is_active":{"type":"boolean"},"source":{"type":"string"},"result_count":{"type":"integer"}},"required":["id","name","name_ar","description","category","scoring_type","is_active","source","result_count"]}},"required":["benchmark"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"workoutBenchmarkAdmin.destroy","tags":["Fitness & Workouts"],"parameters":[{"name":"benchmark","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-items/{item}":{"patch":{"operationId":"workoutContentAdmin.updateItem","summary":"PATCH /manage/workout-items/{item} \u2014 curation flags + metadata touch-ups","tags":["Fitness & Workouts"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","maxLength":200},"title_ar":{"type":["string","null"],"maxLength":200},"description":{"type":["string","null"],"description":"English content \u2014 editable for AUTHORED items (guided-workout: description \u2192 steps, +muscle/equipment/media).","maxLength":5000},"description_ar":{"type":["string","null"],"maxLength":5000},"category":{"type":["string","null"],"maxLength":60},"category_ar":{"type":["string","null"],"maxLength":60},"media_url":{"type":["string","null"],"maxLength":500},"thumbnail_url":{"type":["string","null"],"maxLength":500},"level":{"type":["string","null"],"enum":["beginner","intermediate","advanced"]},"duration_seconds":{"type":["integer","null"],"minimum":1},"is_featured":{"type":"boolean"},"is_hidden":{"type":"boolean"},"is_premium":{"type":"boolean"},"sort":{"type":"integer","minimum":0,"maximum":9999},"equipment":{"type":["array","null"],"items":{"type":"string","maxLength":60}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"thumbnail_url":{"description":"Licensed assets go through the media proxy (the provider key never leaves the server).","anyOf":[{"type":"string"},{"type":"null"}]},"duration_seconds":{"type":["integer","null"]},"level":{"type":["string","null"]},"category":{"type":["string","null"]},"instructor":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"equipment":{"type":"array","items":{}},"media_url":{"type":["string","null"]},"playback":{"type":["object","null"],"description":"Server-resolved playback envelope \u2014 the staff preview plays exactly what\nmembers will (youtube embed for video items, none for inline GIFs).","properties":{"kind":{"type":"string"},"url":{"type":["string","null"]},"expires_at":{"type":["string","null"]}},"required":["kind","url","expires_at"]},"is_premium":{"type":"boolean"},"is_featured":{"type":"boolean"},"is_hidden":{"type":"boolean"},"in_hub":{"type":"boolean"},"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]}},"sort":{"type":"integer"},"synced_at":{"type":"string"}},"required":["id","provider","type","title","title_ar","thumbnail_url","duration_seconds","level","category","instructor","description","description_ar","category_ar","equipment","media_url","playback","is_premium","is_featured","is_hidden","in_hub","groups","sort","synced_at"]}},"required":["item"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"workoutContentAdmin.destroyItem","summary":"DELETE /manage/workout-items/{item} \u2014 remove an authored item (synced items re-appear on sync; hide those instead)","tags":["Fitness & Workouts"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/fitness/routines":{"get":{"operationId":"fitnessRoutine.index","tags":["Fitness & Workouts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"focus":{"type":["string","null"]},"focus_ar":{"type":["string","null"]},"level":{"type":["string","null"]},"level_ar":{"type":["string","null"]},"exercise_count":{"type":"integer"},"duration_min":{"type":"integer"},"volume_kg":{"type":"integer"},"calories":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","focus","focus_ar","level","level_ar","exercise_count","duration_min","volume_kg","calories","sort_order"]}}},"required":["routines"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"fitnessRoutine.store","tags":["Fitness & Workouts"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"routine":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"focus":{"type":["string","null"]},"focus_ar":{"type":["string","null"]},"level":{"type":["string","null"]},"level_ar":{"type":["string","null"]},"exercise_count":{"type":"integer"},"duration_min":{"type":"integer"},"volume_kg":{"type":"integer"},"calories":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","focus","focus_ar","level","level_ar","exercise_count","duration_min","volume_kg","calories","sort_order"]}},"required":["routine"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/fitness/routines/{routine}":{"patch":{"operationId":"fitnessRoutine.update","tags":["Fitness & Workouts"],"parameters":[{"name":"routine","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"routine":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"focus":{"type":["string","null"]},"focus_ar":{"type":["string","null"]},"level":{"type":["string","null"]},"level_ar":{"type":["string","null"]},"exercise_count":{"type":"integer"},"duration_min":{"type":"integer"},"volume_kg":{"type":"integer"},"calories":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","focus","focus_ar","level","level_ar","exercise_count","duration_min","volume_kg","calories","sort_order"]}},"required":["routine"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"fitnessRoutine.destroy","tags":["Fitness & Workouts"],"parameters":[{"name":"routine","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/fitness/exercises":{"get":{"operationId":"fitnessExercise.index","tags":["Fitness & Workouts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"muscle_group":{"type":["string","null"]},"muscle_group_ar":{"type":["string","null"]},"equipment":{"type":["string","null"]},"equipment_ar":{"type":["string","null"]},"difficulty":{"type":"string"},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","muscle_group","muscle_group_ar","equipment","equipment_ar","difficulty","description","description_ar","sort_order"]}}},"required":["exercises"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"fitnessExercise.store","tags":["Fitness & Workouts"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"exercise":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"muscle_group":{"type":["string","null"]},"muscle_group_ar":{"type":["string","null"]},"equipment":{"type":["string","null"]},"equipment_ar":{"type":["string","null"]},"difficulty":{"type":"string"},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","muscle_group","muscle_group_ar","equipment","equipment_ar","difficulty","description","description_ar","sort_order"]}},"required":["exercise"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/fitness/exercises/{exercise}":{"patch":{"operationId":"fitnessExercise.update","tags":["Fitness & Workouts"],"parameters":[{"name":"exercise","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"exercise":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"muscle_group":{"type":["string","null"]},"muscle_group_ar":{"type":["string","null"]},"equipment":{"type":["string","null"]},"equipment_ar":{"type":["string","null"]},"difficulty":{"type":"string"},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","muscle_group","muscle_group_ar","equipment","equipment_ar","difficulty","description","description_ar","sort_order"]}},"required":["exercise"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"fitnessExercise.destroy","tags":["Fitness & Workouts"],"parameters":[{"name":"exercise","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/social/overview":{"get":{"operationId":"socialAdmin.overview","summary":"GET /manage/social/overview \u2014 brand-wide adoption + engagement KPIs, breakdown, trend, top creators","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"kpis":{"type":"object","properties":{"members":{"type":"integer","minimum":0},"profiles":{"type":"integer","minimum":0},"adoption_rate":{"type":"number"},"friendships":{"type":"integer"},"pending_requests":{"type":"integer","description":"rows are mirrored","minimum":0},"blocks":{"type":"integer","minimum":0},"challenges_active":{"type":"integer"},"challenges_pending":{"type":"integer"},"challenges_ended":{"type":"integer"},"participants":{"type":"integer","minimum":0},"participation_rate":{"type":"number"},"avg_players":{"type":"number"},"nudges_30d":{"type":"integer","minimum":0}},"required":["members","profiles","adoption_rate","friendships","pending_requests","blocks","challenges_active","challenges_pending","challenges_ended","participants","participation_rate","avg_players","nudges_30d"]},"metric_breakdown":{"type":"array","items":{"type":"object","properties":{"metric":{"type":"string"},"count":{"type":"integer"}},"required":["metric","count"]}},"trend":{"type":"array","items":{}},"top_creators":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"count":{"type":"integer"}},"required":["member","count"]}}},"required":["kpis","metric_breakdown","trend","top_creators"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/social":{"get":{"operationId":"socialAdmin.settings","summary":"GET /manage/social \u2014 the brand's Social settings (+ the full metric catalogue for the UI)","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"is_active":{"type":"boolean"},"friends_enabled":{"type":"boolean"},"challenges_enabled":{"type":"boolean"},"nudges_enabled":{"type":"boolean"},"allowed_metrics":{"anyOf":[{"type":"array","items":{}},{"type":"array","prefixItems":[{"type":"string","const":"gym_visits"},{"type":"string","const":"classes"},{"type":"string","const":"program_days"},{"type":"string","const":"workouts"},{"type":"string","const":"minutes"},{"type":"string","const":"calories"},{"type":"string","const":"volume_kg"}],"minItems":7,"maxItems":7,"additionalItems":false}]},"max_players":{"type":"integer"},"invite_expiry_hours":{"type":"integer"},"nudge_cooldown_minutes":{"type":"integer"},"all_metrics":{"type":"array","prefixItems":[{"type":"string","const":"gym_visits"},{"type":"string","const":"classes"},{"type":"string","const":"program_days"},{"type":"string","const":"workouts"},{"type":"string","const":"minutes"},{"type":"string","const":"calories"},{"type":"string","const":"volume_kg"}],"minItems":7,"maxItems":7,"additionalItems":false}},"required":["is_active","friends_enabled","challenges_enabled","nudges_enabled","allowed_metrics","max_players","invite_expiry_hours","nudge_cooldown_minutes","all_metrics"]}},"required":["settings"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/social/settings":{"put":{"operationId":"socialAdmin.updateSettings","summary":"PUT /manage/social/settings \u2014 turn the feature (and sub-features) on/off + tune the guardrails","tags":["Loyalty & Community"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"is_active":{"type":"boolean"},"friends_enabled":{"type":"boolean"},"challenges_enabled":{"type":"boolean"},"nudges_enabled":{"type":"boolean"},"max_players":{"type":"integer","minimum":2,"maximum":50},"invite_expiry_hours":{"type":"integer","minimum":1,"maximum":336},"nudge_cooldown_minutes":{"type":"integer","minimum":1,"maximum":1440},"allowed_metrics":{"type":["array","null"],"items":{"type":"string","enum":["gym_visits","classes","program_days","workouts","minutes","calories","volume_kg"]}}},"required":["is_active","friends_enabled","challenges_enabled","nudges_enabled","max_players","invite_expiry_hours","nudge_cooldown_minutes"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/social/challenges":{"get":{"operationId":"socialAdmin.challenges","summary":"GET /manage/social/challenges \u2014 the brand's challenges (paginated + filtered) for the console","tags":["Loyalty & Community"],"parameters":[{"name":"status","in":"query","schema":{"type":["string","null"]}},{"name":"metric","in":"query","schema":{"type":["string","null"]}},{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":60}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"created_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"players":{"type":"integer","minimum":0},"accepted":{"type":"integer","minimum":0}},"required":["id","metric","status","duration_days","starts_at","ends_at","created_at","creator","players","accepted"]}},"meta":{"type":"object","properties":{"current_page":{"type":"integer"},"last_page":{"type":"integer"},"total":{"type":"integer"},"per_page":{"type":"integer"}},"required":["current_page","last_page","total","per_page"]}},"required":["data","meta"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/social/challenges/{challenge}":{"get":{"operationId":"socialAdmin.challenge","summary":"GET /manage/social/challenges/{challenge} \u2014 full detail + live leaderboard","tags":["Loyalty & Community"],"parameters":[{"name":"challenge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"participants":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"score":{"type":"integer"},"accepted":{"type":"boolean"}},"required":["member","score","accepted"]}}},"required":["id","metric","status","duration_days","starts_at","ends_at","creator","participants"]}},"required":["challenge"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/social/challenges/{challenge}/end":{"post":{"operationId":"socialAdmin.endChallenge","summary":"POST /manage/social/challenges/{challenge}/end \u2014 force-finish (settle active / cancel pending)","tags":["Loyalty & Community"],"parameters":[{"name":"challenge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"participants":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"score":{"type":"integer"},"accepted":{"type":"boolean"}},"required":["member","score","accepted"]}}},"required":["id","metric","status","duration_days","starts_at","ends_at","creator","participants"]}},"required":["challenge"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/referrals":{"get":{"operationId":"referral.index","summary":"List the current brand's referrals","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"referrals":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"referrer_member_id":{"type":"string"},"code":{"type":"string"},"referee_member_id":{"type":["string","null"]},"status":{"type":"string"},"reward_minor":{"type":"integer"}},"required":["id","referrer_member_id","code","referee_member_id","status","reward_minor"]}}},"required":["referrals"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/referrals/convert":{"post":{"operationId":"referral.convert","summary":"Attribute a signup to a code and flip it to `converted`","tags":["Loyalty & Community"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"referee_member_id":{"type":"string"}},"required":["code","referee_member_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"referrer_member_id":{"type":"string"},"code":{"type":"string"},"referee_member_id":{"type":["string","null"]},"status":{"type":"string"},"reward_minor":{"type":"integer"}},"required":["id","referrer_member_id","code","referee_member_id","status","reward_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/loyalty":{"get":{"operationId":"loyalty.index","summary":"The full programme config: earn settings + the tier ladder low\u2192high","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"settings":{"type":"object","properties":{"points_per_riyal":{"type":"integer"},"points_per_checkin":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["points_per_riyal","points_per_checkin","is_active"]},"tiers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"min_lifetime_points":{"type":"integer"},"cashback_percent":{"type":"integer"},"points_multiplier":{"type":"number"},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","min_lifetime_points","cashback_percent","points_multiplier","sort_order","is_active"]}}},"required":["settings","tiers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/loyalty/settings":{"put":{"operationId":"loyalty.updateSettings","summary":"Update the brand's earn settings","tags":["Loyalty & Community"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"points_per_riyal":{"type":"integer","minimum":0,"maximum":1000},"points_per_checkin":{"type":"integer","minimum":0,"maximum":100000},"is_active":{"type":"boolean"}},"required":["points_per_riyal","points_per_checkin","is_active"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/loyalty/tiers":{"post":{"operationId":"loyalty.storeTier","tags":["Loyalty & Community"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tier":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"min_lifetime_points":{"type":"integer"},"cashback_percent":{"type":"integer"},"points_multiplier":{"type":"number"},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","min_lifetime_points","cashback_percent","points_multiplier","sort_order","is_active"]}},"required":["tier"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/loyalty/tiers/{tier}":{"patch":{"operationId":"loyalty.updateTier","tags":["Loyalty & Community"],"parameters":[{"name":"tier","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tier":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"min_lifetime_points":{"type":"integer"},"cashback_percent":{"type":"integer"},"points_multiplier":{"type":"number"},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","min_lifetime_points","cashback_percent","points_multiplier","sort_order","is_active"]}},"required":["tier"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"loyalty.destroyTier","tags":["Loyalty & Community"],"parameters":[{"name":"tier","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/challenges":{"get":{"operationId":"challenge.index","summary":"List challenges with this-period completion counts","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"metric":{"type":"string"},"target":{"type":"integer"},"period_type":{"type":"string"},"starts_on":{"type":"string"},"ends_on":{"type":"string"},"reward_type":{"type":"string"},"reward_value":{"type":"integer"},"reward_reference_id":{"type":["string","null"]},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"},"completions":{"type":"integer"}},"required":["id","name","name_ar","description","description_ar","metric","target","period_type","starts_on","ends_on","reward_type","reward_value","reward_reference_id","sort_order","is_active","completions"]}}},"required":["challenges"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"challenge.store","tags":["Loyalty & Community"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"metric":{"type":"string"},"target":{"type":"integer"},"period_type":{"type":"string"},"starts_on":{"type":"string"},"ends_on":{"type":"string"},"reward_type":{"type":"string"},"reward_value":{"type":"integer"},"reward_reference_id":{"type":["string","null"]},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"},"completions":{"type":"integer"}},"required":["id","name","name_ar","description","description_ar","metric","target","period_type","starts_on","ends_on","reward_type","reward_value","reward_reference_id","sort_order","is_active","completions"]}},"required":["challenge"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/challenges/{challenge}":{"patch":{"operationId":"challenge.update","tags":["Loyalty & Community"],"parameters":[{"name":"challenge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"metric":{"type":"string"},"target":{"type":"integer"},"period_type":{"type":"string"},"starts_on":{"type":"string"},"ends_on":{"type":"string"},"reward_type":{"type":"string"},"reward_value":{"type":"integer"},"reward_reference_id":{"type":["string","null"]},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"},"completions":{"type":"integer"}},"required":["id","name","name_ar","description","description_ar","metric","target","period_type","starts_on","ends_on","reward_type","reward_value","reward_reference_id","sort_order","is_active","completions"]}},"required":["challenge"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"challenge.destroy","tags":["Loyalty & Community"],"parameters":[{"name":"challenge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/groups":{"get":{"operationId":"communityGroup.index","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"members_count":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","description","description_ar","members_count","sort_order"]}}},"required":["groups"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"communityGroup.store","tags":["Loyalty & Community"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"group":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"members_count":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","description","description_ar","members_count","sort_order"]}},"required":["group"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/groups/{group}":{"patch":{"operationId":"communityGroup.update","tags":["Loyalty & Community"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"group":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"members_count":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","description","description_ar","members_count","sort_order"]}},"required":["group"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"communityGroup.destroy","tags":["Loyalty & Community"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/challenges":{"get":{"operationId":"communityChallenge.index","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"unit":{"type":["string","null"]},"unit_ar":{"type":["string","null"]},"goal":{"type":"integer"},"participants_count":{"type":"integer"},"ends_at":{"type":"string"},"reward_points":{"type":"integer"},"reward_minor":{"type":"integer"},"is_active":{"type":"boolean"},"sort_order":{"type":"integer"}},"required":["id","icon_key","title","title_ar","description","description_ar","unit","unit_ar","goal","participants_count","ends_at","reward_points","reward_minor","is_active","sort_order"]}}},"required":["challenges"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"communityChallenge.store","tags":["Loyalty & Community"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"unit":{"type":["string","null"]},"unit_ar":{"type":["string","null"]},"goal":{"type":"integer"},"participants_count":{"type":"integer"},"ends_at":{"type":"string"},"reward_points":{"type":"integer"},"reward_minor":{"type":"integer"},"is_active":{"type":"boolean"},"sort_order":{"type":"integer"}},"required":["id","icon_key","title","title_ar","description","description_ar","unit","unit_ar","goal","participants_count","ends_at","reward_points","reward_minor","is_active","sort_order"]}},"required":["challenge"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/challenges/{challenge}":{"patch":{"operationId":"communityChallenge.update","tags":["Loyalty & Community"],"parameters":[{"name":"challenge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"unit":{"type":["string","null"]},"unit_ar":{"type":["string","null"]},"goal":{"type":"integer"},"participants_count":{"type":"integer"},"ends_at":{"type":"string"},"reward_points":{"type":"integer"},"reward_minor":{"type":"integer"},"is_active":{"type":"boolean"},"sort_order":{"type":"integer"}},"required":["id","icon_key","title","title_ar","description","description_ar","unit","unit_ar","goal","participants_count","ends_at","reward_points","reward_minor","is_active","sort_order"]}},"required":["challenge"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"communityChallenge.destroy","tags":["Loyalty & Community"],"parameters":[{"name":"challenge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/badges":{"get":{"operationId":"communityBadge.index","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"badges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"metric":{"type":"string"},"threshold":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","metric","threshold","sort_order"]}}},"required":["badges"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"communityBadge.store","tags":["Loyalty & Community"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"badge":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"metric":{"type":"string"},"threshold":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","metric","threshold","sort_order"]}},"required":["badge"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/badges/{badge}":{"patch":{"operationId":"communityBadge.update","tags":["Loyalty & Community"],"parameters":[{"name":"badge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"badge":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"metric":{"type":"string"},"threshold":{"type":"integer"},"sort_order":{"type":"integer"}},"required":["id","icon_key","name","name_ar","metric","threshold","sort_order"]}},"required":["badge"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"communityBadge.destroy","tags":["Loyalty & Community"],"parameters":[{"name":"badge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/posts":{"get":{"operationId":"communityPost.index","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"author_name":{"type":"string"},"author_name_ar":{"type":["string","null"]},"group_name":{"type":["string","null"]},"group_name_ar":{"type":["string","null"]},"kind":{"type":"string"},"pinned":{"type":"boolean"},"author_is_coach":{"type":"boolean"},"body":{"type":"string"},"body_ar":{"type":["string","null"]},"likes_count":{"type":"integer"},"comments_count":{"type":"integer"},"created_at":{"type":"string"}},"required":["id","author_name","author_name_ar","group_name","group_name_ar","kind","pinned","author_is_coach","body","body_ar","likes_count","comments_count","created_at"]}}},"required":["posts"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"communityPost.store","tags":["Loyalty & Community"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"post":{"type":"object","properties":{"id":{"type":"string"},"author_name":{"type":"string"},"author_name_ar":{"type":["string","null"]},"group_name":{"type":["string","null"]},"group_name_ar":{"type":["string","null"]},"kind":{"type":"string"},"pinned":{"type":"boolean"},"author_is_coach":{"type":"boolean"},"body":{"type":"string"},"body_ar":{"type":["string","null"]},"likes_count":{"type":"integer"},"comments_count":{"type":"integer"},"created_at":{"type":"string"}},"required":["id","author_name","author_name_ar","group_name","group_name_ar","kind","pinned","author_is_coach","body","body_ar","likes_count","comments_count","created_at"]}},"required":["post"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/posts/{post}":{"patch":{"operationId":"communityPost.update","tags":["Loyalty & Community"],"parameters":[{"name":"post","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"post":{"type":"object","properties":{"id":{"type":"string"},"author_name":{"type":"string"},"author_name_ar":{"type":["string","null"]},"group_name":{"type":["string","null"]},"group_name_ar":{"type":["string","null"]},"kind":{"type":"string"},"pinned":{"type":"boolean"},"author_is_coach":{"type":"boolean"},"body":{"type":"string"},"body_ar":{"type":["string","null"]},"likes_count":{"type":"integer"},"comments_count":{"type":"integer"},"created_at":{"type":"string"}},"required":["id","author_name","author_name_ar","group_name","group_name_ar","kind","pinned","author_is_coach","body","body_ar","likes_count","comments_count","created_at"]}},"required":["post"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"communityPost.destroy","tags":["Loyalty & Community"],"parameters":[{"name":"post","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/reports":{"get":{"operationId":"communityReport.index","tags":["Loyalty & Community"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"open_count":{"type":"integer","minimum":0},"reports":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"target_type":{"type":"string"},"target_id":{"type":"string"},"reason":{"type":["string","null"]},"status":{"type":"string"},"created_at":{"type":"string"},"reporter":{"type":"string"},"content":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"comment"},"body":{"type":"string"},"author_name":{"type":"string"},"exists":{"type":"boolean"}},"required":["kind","body","author_name","exists"]},{"type":"object","properties":{"kind":{"type":"string","const":"comment"},"body":{"type":"null"},"author_name":{"type":"null"},"exists":{"type":"boolean"}},"required":["kind","body","author_name","exists"]}]}},"required":["id","target_type","target_id","reason","status","created_at","reporter","content"]}}},"required":["open_count","reports"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/reports/{report}/resolve":{"post":{"operationId":"communityReport.resolve","tags":["Loyalty & Community"],"parameters":[{"name":"report","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["dismiss","remove"]}},"required":["action"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/community/members/{member}/coach":{"patch":{"operationId":"communityMember.setCoach","summary":"PATCH /manage/community/members/{member}/coach \u2014 mark (or unmark) a member as a community coach","tags":["Loyalty & Community"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"is_coach":{"type":"boolean"}},"required":["is_coach"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"is_coach":{"type":"boolean"}},"required":["ok","is_coach"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/track/o/{token}.gif":{"get":{"operationId":"emailTracking.open","tags":["Marketing & Campaigns"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"image/gif":{"schema":{"anyOf":[{"type":"string"},{"type":"object"}]}}},"headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store, no-cache, must-revalidate"}},"Pragma":{"schema":{"type":"string","const":"no-cache"}}}}}}},"/track/open/{token}.gif":{"get":{"operationId":"campaignTracking.open","summary":"GET /track/open/{token}.gif \u2014 mark the recipient opened, return the pixel","tags":["Marketing & Campaigns"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"string"},{"type":"object"}]}}}}}}},"/track/click/{token}":{"get":{"operationId":"campaignTracking.click","summary":"GET /track/click/{token} \u2014 mark clicked (implies opened), redirect to the offer landing page","tags":["Marketing & Campaigns"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}}}},"/automations":{"get":{"operationId":"automation.index","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trigger":{"type":"string"},"conditions":{"type":"array","items":{}},"action":{"type":"string"},"action_config":{"type":"array","items":{}},"is_active":{"type":"boolean"},"last_run_at":{"type":"string"},"fired_count":{"type":"integer"},"recent_fires":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"attributed_revenue_minor":{"type":"integer"}},"required":["id","name","trigger","conditions","action","action_config","is_active","last_run_at","fired_count","recent_fires","conversions","attributed_revenue_minor"]}},"triggers":{"type":"array","prefixItems":[{"type":"string","const":"contract_expiring"},{"type":"string","const":"member_inactive"},{"type":"string","const":"birthday"},{"type":"string","const":"after_subscribe"},{"type":"string","const":"membership_lapsed"},{"type":"string","const":"freeze_ending"},{"type":"string","const":"anniversary"},{"type":"string","const":"first_check_in"},{"type":"string","const":"payment_failed"},{"type":"string","const":"pt_credits_low"},{"type":"string","const":"dropped_cart"}],"minItems":11,"maxItems":11,"additionalItems":false},"actions":{"type":"array","prefixItems":[{"type":"string","const":"create_task"},{"type":"string","const":"notify_member"},{"type":"string","const":"send_email"}],"minItems":3,"maxItems":3,"additionalItems":false}},"required":["rules","triggers","actions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"automation.store","tags":["Marketing & Campaigns"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trigger":{"type":"string"},"conditions":{"type":"array","items":{}},"action":{"type":"string"},"action_config":{"type":"array","items":{}},"is_active":{"type":"boolean"},"last_run_at":{"type":"string"},"fired_count":{"type":"integer"},"recent_fires":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"attributed_revenue_minor":{"type":"integer"}},"required":["id","name","trigger","conditions","action","action_config","is_active","last_run_at","fired_count","recent_fires","conversions","attributed_revenue_minor"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/automations/run":{"post":{"operationId":"automation.run","summary":"POST /automations/run \u2014 fire the engine now (across this brand's rules)","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rules":{"type":"integer"},"sequences":{"type":"integer"},"fired":{"type":"integer"}},"required":["rules","sequences","fired"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/automations/{rule}":{"put":{"operationId":"automation.update","tags":["Marketing & Campaigns"],"parameters":[{"name":"rule","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trigger":{"type":"string"},"conditions":{"type":"array","items":{}},"action":{"type":"string"},"action_config":{"type":"array","items":{}},"is_active":{"type":"boolean"},"last_run_at":{"type":"string"},"fired_count":{"type":"integer"},"recent_fires":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"attributed_revenue_minor":{"type":"integer"}},"required":["id","name","trigger","conditions","action","action_config","is_active","last_run_at","fired_count","recent_fires","conversions","attributed_revenue_minor"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"automation.destroy","tags":["Marketing & Campaigns"],"parameters":[{"name":"rule","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/automations/{rule}/toggle":{"post":{"operationId":"automation.toggle","tags":["Marketing & Campaigns"],"parameters":[{"name":"rule","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trigger":{"type":"string"},"conditions":{"type":"array","items":{}},"action":{"type":"string"},"action_config":{"type":"array","items":{}},"is_active":{"type":"boolean"},"last_run_at":{"type":"string"},"fired_count":{"type":"integer"},"recent_fires":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"attributed_revenue_minor":{"type":"integer"}},"required":["id","name","trigger","conditions","action","action_config","is_active","last_run_at","fired_count","recent_fires","conversions","attributed_revenue_minor"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/email-templates":{"get":{"operationId":"emailTemplate.index_0","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"subject":{"type":"string"},"subject_ar":{"type":["string","null"]},"body":{"type":"string"},"body_ar":{"type":["string","null"]},"cta_label":{"type":["string","null"]},"cta_label_ar":{"type":["string","null"]},"cta_url":{"type":["string","null"]}},"required":["id","name","subject","subject_ar","body","body_ar","cta_label","cta_label_ar","cta_url"]}}},"required":["templates"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"emailTemplate.store","tags":["Marketing & Campaigns"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"subject":{"type":"string"},"subject_ar":{"type":["string","null"]},"body":{"type":"string"},"body_ar":{"type":["string","null"]},"cta_label":{"type":["string","null"]},"cta_label_ar":{"type":["string","null"]},"cta_url":{"type":["string","null"]}},"required":["id","name","subject","subject_ar","body","body_ar","cta_label","cta_label_ar","cta_url"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/email-templates/{template}":{"put":{"operationId":"emailTemplate.update","tags":["Marketing & Campaigns"],"parameters":[{"name":"template","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"subject":{"type":"string"},"subject_ar":{"type":["string","null"]},"body":{"type":"string"},"body_ar":{"type":["string","null"]},"cta_label":{"type":["string","null"]},"cta_label_ar":{"type":["string","null"]},"cta_url":{"type":["string","null"]}},"required":["id","name","subject","subject_ar","body","body_ar","cta_label","cta_label_ar","cta_url"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"emailTemplate.destroy","tags":["Marketing & Campaigns"],"parameters":[{"name":"template","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/automation-sequences":{"get":{"operationId":"automationSequence.index","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sequences":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trigger":{"type":"string"},"conditions":{"type":"array","items":{}},"stop_on_convert":{"type":"boolean"},"is_active":{"type":"boolean"},"steps":{"type":"array","items":{"type":"object","properties":{"offset_days":{"type":"integer"},"action":{"type":"string"},"action_config":{"type":"array","items":{}},"condition":{"type":["string","null"]},"condition_negate":{"type":"boolean"},"condition_action":{"type":"string"}},"required":["offset_days","action","action_config","condition","condition_negate","condition_action"]}},"active_enrollments":{"type":"integer","minimum":0},"completed":{"type":"integer","minimum":0},"exited":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"attributed_revenue_minor":{"type":"integer"}},"required":["id","name","trigger","conditions","stop_on_convert","is_active","steps","active_enrollments","completed","exited","conversions","attributed_revenue_minor"]}},"triggers":{"type":"array","prefixItems":[{"type":"string","const":"contract_expiring"},{"type":"string","const":"member_inactive"},{"type":"string","const":"birthday"},{"type":"string","const":"after_subscribe"},{"type":"string","const":"membership_lapsed"},{"type":"string","const":"freeze_ending"},{"type":"string","const":"anniversary"},{"type":"string","const":"first_check_in"},{"type":"string","const":"payment_failed"},{"type":"string","const":"pt_credits_low"},{"type":"string","const":"dropped_cart"}],"minItems":11,"maxItems":11,"additionalItems":false},"actions":{"type":"array","prefixItems":[{"type":"string","const":"create_task"},{"type":"string","const":"notify_member"},{"type":"string","const":"send_email"}],"minItems":3,"maxItems":3,"additionalItems":false},"conditions":{"type":"array","prefixItems":[{"type":"string","const":"has_active_membership"},{"type":"string","const":"recently_active"},{"type":"string","const":"paid_since_enrolled"},{"type":"string","const":"has_outstanding_dues"},{"type":"string","const":"email_opened"}],"minItems":5,"maxItems":5,"additionalItems":false},"condition_actions":{"type":"array","prefixItems":[{"type":"string","const":"skip"},{"type":"string","const":"exit"}],"minItems":2,"maxItems":2,"additionalItems":false}},"required":["sequences","triggers","actions","conditions","condition_actions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"automationSequence.store","tags":["Marketing & Campaigns"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trigger":{"type":"string"},"conditions":{"type":"array","items":{}},"stop_on_convert":{"type":"boolean"},"is_active":{"type":"boolean"},"steps":{"type":"array","items":{"type":"object","properties":{"offset_days":{"type":"integer"},"action":{"type":"string"},"action_config":{"type":"array","items":{}},"condition":{"type":["string","null"]},"condition_negate":{"type":"boolean"},"condition_action":{"type":"string"}},"required":["offset_days","action","action_config","condition","condition_negate","condition_action"]}},"active_enrollments":{"type":"integer","minimum":0},"completed":{"type":"integer","minimum":0},"exited":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"attributed_revenue_minor":{"type":"integer"}},"required":["id","name","trigger","conditions","stop_on_convert","is_active","steps","active_enrollments","completed","exited","conversions","attributed_revenue_minor"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/automation-sequences/{sequence}":{"put":{"operationId":"automationSequence.update","tags":["Marketing & Campaigns"],"parameters":[{"name":"sequence","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trigger":{"type":"string"},"conditions":{"type":"array","items":{}},"stop_on_convert":{"type":"boolean"},"is_active":{"type":"boolean"},"steps":{"type":"array","items":{"type":"object","properties":{"offset_days":{"type":"integer"},"action":{"type":"string"},"action_config":{"type":"array","items":{}},"condition":{"type":["string","null"]},"condition_negate":{"type":"boolean"},"condition_action":{"type":"string"}},"required":["offset_days","action","action_config","condition","condition_negate","condition_action"]}},"active_enrollments":{"type":"integer","minimum":0},"completed":{"type":"integer","minimum":0},"exited":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"attributed_revenue_minor":{"type":"integer"}},"required":["id","name","trigger","conditions","stop_on_convert","is_active","steps","active_enrollments","completed","exited","conversions","attributed_revenue_minor"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"automationSequence.destroy","tags":["Marketing & Campaigns"],"parameters":[{"name":"sequence","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/automation-sequences/{sequence}/toggle":{"post":{"operationId":"automationSequence.toggle","tags":["Marketing & Campaigns"],"parameters":[{"name":"sequence","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"trigger":{"type":"string"},"conditions":{"type":"array","items":{}},"stop_on_convert":{"type":"boolean"},"is_active":{"type":"boolean"},"steps":{"type":"array","items":{"type":"object","properties":{"offset_days":{"type":"integer"},"action":{"type":"string"},"action_config":{"type":"array","items":{}},"condition":{"type":["string","null"]},"condition_negate":{"type":"boolean"},"condition_action":{"type":"string"}},"required":["offset_days","action","action_config","condition","condition_negate","condition_action"]}},"active_enrollments":{"type":"integer","minimum":0},"completed":{"type":"integer","minimum":0},"exited":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"attributed_revenue_minor":{"type":"integer"}},"required":["id","name","trigger","conditions","stop_on_convert","is_active","steps","active_enrollments","completed","exited","conversions","attributed_revenue_minor"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/hero-slides/upload":{"post":{"operationId":"heroSlide.upload","summary":"POST /hero-slides/upload \u2014 store an image/video and return its public URL","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","description":"\u226450 MB; images + the common web video containers.","maxLength":51200}},"required":["file"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"path":{"type":"string"},"disk":{"type":"string","enum":["s3","public"]}},"required":["url","path","disk"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/hero-slides/reorder":{"post":{"operationId":"heroSlide.reorder","summary":"POST /hero-slides/reorder {ids:[...]} \u2014 persist the new slide order","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["ids"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/hero-slides":{"get":{"operationId":"heroSlide.index","summary":"GET /hero-slides \u2014 the brand's slides (staff, ordered)","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"slides":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"media_url":{"type":"string"},"poster_url":{"type":["string","null"]},"headline":{"type":["string","null"]},"headline_ar":{"type":["string","null"]},"subcopy":{"type":["string","null"]},"subcopy_ar":{"type":["string","null"]},"cta_label":{"type":["string","null"]},"cta_label_ar":{"type":["string","null"]},"cta_href":{"type":["string","null"]},"sort":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","type","media_url","poster_url","headline","headline_ar","subcopy","subcopy_ar","cta_label","cta_label_ar","cta_href","sort","is_active"]}}},"required":["slides"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"heroSlide.store","tags":["Marketing & Campaigns"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"media_url":{"type":"string"},"poster_url":{"type":["string","null"]},"headline":{"type":["string","null"]},"headline_ar":{"type":["string","null"]},"subcopy":{"type":["string","null"]},"subcopy_ar":{"type":["string","null"]},"cta_label":{"type":["string","null"]},"cta_label_ar":{"type":["string","null"]},"cta_href":{"type":["string","null"]},"sort":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","type","media_url","poster_url","headline","headline_ar","subcopy","subcopy_ar","cta_label","cta_label_ar","cta_href","sort","is_active"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/hero-slides/{slide}":{"put":{"operationId":"heroSlide.update","tags":["Marketing & Campaigns"],"parameters":[{"name":"slide","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"media_url":{"type":"string"},"poster_url":{"type":["string","null"]},"headline":{"type":["string","null"]},"headline_ar":{"type":["string","null"]},"subcopy":{"type":["string","null"]},"subcopy_ar":{"type":["string","null"]},"cta_label":{"type":["string","null"]},"cta_label_ar":{"type":["string","null"]},"cta_href":{"type":["string","null"]},"sort":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","type","media_url","poster_url","headline","headline_ar","subcopy","subcopy_ar","cta_label","cta_label_ar","cta_href","sort","is_active"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"heroSlide.destroy","tags":["Marketing & Campaigns"],"parameters":[{"name":"slide","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/promotions":{"get":{"operationId":"promotion.index","summary":"List all of the brand's promotions (active and inactive)","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"promotions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"discount_type":{"type":"string"},"amount":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"max_redemptions":{"type":["integer","null"]},"redemptions_used":{"type":"integer"},"is_active":{"type":"boolean"},"stackable":{"type":"boolean"},"applies_to_plan_id":{"type":["string","null"]},"eligibility_segment":{"type":["string","null"]}},"required":["id","code","name","name_ar","discount_type","amount","starts_at","ends_at","max_redemptions","redemptions_used","is_active","stackable","applies_to_plan_id","eligibility_segment"]}}},"required":["promotions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"promotion.store","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"discount_type":{"$ref":"#/components/schemas/DiscountType"},"amount":{"type":"integer","minimum":0,"maximum":100000000},"starts_at":{"type":["string","null"],"format":"date-time"},"ends_at":{"type":["string","null"],"format":"date-time"},"max_redemptions":{"type":["integer","null"],"minimum":1},"is_active":{"type":"boolean"},"stackable":{"type":"boolean"},"applies_to_plan_id":{"type":["string","null"]},"eligibility_segment":{"type":["string","null"]}},"required":["code","name","discount_type","amount"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"discount_type":{"type":"string"},"amount":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"max_redemptions":{"type":["integer","null"]},"redemptions_used":{"type":"integer"},"is_active":{"type":"boolean"},"stackable":{"type":"boolean"},"applies_to_plan_id":{"type":["string","null"]},"eligibility_segment":{"type":["string","null"]}},"required":["id","code","name","name_ar","discount_type","amount","starts_at","ends_at","max_redemptions","redemptions_used","is_active","stackable","applies_to_plan_id","eligibility_segment"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/promotions/bulk":{"post":{"operationId":"promotion.bulkGenerate","summary":"Bulk-generate promotion codes (RFP 01 #10). Creates <count> promotions\nsharing a discount configuration, each with a unique `<prefix><random>`\ncode, and returns the created codes. `count` is capped at 500","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prefix":{"type":"string","maxLength":20},"count":{"type":"integer","minimum":1,"maximum":500},"discount_type":{"$ref":"#/components/schemas/DiscountType"},"amount":{"type":"integer","minimum":0,"maximum":100000000},"eligibility_segment":{"type":["string","null"]},"max_redemptions":{"type":["integer","null"],"minimum":1},"ends_at":{"type":["string","null"],"format":"date-time"}},"required":["prefix","count","discount_type","amount"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"},"codes":{"type":"array","items":{"type":"string"}}},"required":["count","codes"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/promotions/{promotion}":{"patch":{"operationId":"promotion.update","summary":"Toggle or edit a promotion","tags":["Marketing & Campaigns"],"parameters":[{"name":"promotion","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"discount_type":{"$ref":"#/components/schemas/DiscountType"},"amount":{"type":"integer","minimum":0},"starts_at":{"type":["string","null"],"format":"date-time"},"ends_at":{"type":["string","null"],"format":"date-time"},"max_redemptions":{"type":["integer","null"],"minimum":1},"is_active":{"type":"boolean"},"applies_to_plan_id":{"type":["string","null"]},"eligibility_segment":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"discount_type":{"type":"string"},"amount":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"max_redemptions":{"type":["integer","null"]},"redemptions_used":{"type":"integer"},"is_active":{"type":"boolean"},"stackable":{"type":"boolean"},"applies_to_plan_id":{"type":["string","null"]},"eligibility_segment":{"type":["string","null"]}},"required":["id","code","name","name_ar","discount_type","amount","starts_at","ends_at","max_redemptions","redemptions_used","is_active","stackable","applies_to_plan_id","eligibility_segment"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/promotions/validate":{"post":{"operationId":"promotion.validateCode","summary":"Validate a promo code against a gross amount, returning the discount + net","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"plan_id":{"type":["string","null"]},"amount_minor":{"type":"integer","minimum":0},"segment":{"type":["string","null"]}},"required":["code","amount_minor"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"discount_minor":{"type":"string"},"net_minor":{"type":"string"},"extension_days":{"type":"string"},"valid":{"type":"boolean"}},"required":["code","discount_minor","net_minor","extension_days","valid"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/promotions/validate-stack":{"post":{"operationId":"promotion.validateStack","summary":"POST /promotions/validate-stack \u2014 validate + combine MULTIPLE codes with stacking rules","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan_id":{"type":["string","null"]},"amount_minor":{"type":"integer","minimum":0},"segment":{"type":["string","null"]},"codes":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["amount_minor","codes"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"codes":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"discount_minor":{"type":"integer"},"extension_days":{"type":"integer"}},"required":["code","discount_minor","extension_days"]}},"total_discount_minor":{"type":"integer"},"net_minor":{"type":"string"},"extension_days":{"type":"integer"},"valid":{"type":"boolean"}},"required":["codes","total_discount_minor","net_minor","extension_days","valid"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/coupons":{"get":{"operationId":"coupon.index","summary":"List all of the brand's coupons (active and inactive)","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"coupons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"discount_type":{"type":"string"},"amount":{"type":"integer"},"max_discount_minor":{"type":["integer","null"]},"scope":{"type":"string"},"product_ids":{"type":["array","null"],"items":{}},"min_subtotal_minor":{"type":["integer","null"]},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"max_redemptions":{"type":["integer","null"]},"redemptions_used":{"type":"integer"},"per_customer_limit":{"type":["integer","null"]},"is_active":{"type":"boolean"}},"required":["id","code","name","name_ar","discount_type","amount","max_discount_minor","scope","product_ids","min_subtotal_minor","valid_from","valid_to","max_redemptions","redemptions_used","per_customer_limit","is_active"]}}},"required":["coupons"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"coupon.store","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"discount_type":{"$ref":"#/components/schemas/CouponDiscountType"},"amount":{"type":"integer","minimum":0,"maximum":100000000},"max_discount_minor":{"type":["integer","null"],"minimum":0},"scope":{"$ref":"#/components/schemas/CouponScope"},"min_subtotal_minor":{"type":["integer","null"],"minimum":0},"valid_from":{"type":["string","null"],"format":"date-time"},"valid_to":{"type":["string","null"],"format":"date-time"},"max_redemptions":{"type":["integer","null"],"minimum":1},"per_customer_limit":{"type":["integer","null"],"minimum":1},"is_active":{"type":"boolean"},"stackable":{"type":"boolean","description":"RFP 1.7 promo-code rules: stack with other stackable codes + restrict to a channel."},"channel":{"type":["string","null"],"enum":["email","sms","push","pos","online"]},"product_ids":{"type":["array","null"],"items":{"type":"string"}}},"required":["code","name","discount_type","amount","scope"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"discount_type":{"type":"string"},"amount":{"type":"integer"},"max_discount_minor":{"type":["integer","null"]},"scope":{"type":"string"},"product_ids":{"type":["array","null"],"items":{}},"min_subtotal_minor":{"type":["integer","null"]},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"max_redemptions":{"type":["integer","null"]},"redemptions_used":{"type":"integer"},"per_customer_limit":{"type":["integer","null"]},"is_active":{"type":"boolean"}},"required":["id","code","name","name_ar","discount_type","amount","max_discount_minor","scope","product_ids","min_subtotal_minor","valid_from","valid_to","max_redemptions","redemptions_used","per_customer_limit","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/coupons/bulk":{"post":{"operationId":"coupon.bulkGenerate","summary":"Bulk-generate coupon codes (mirrors PromotionController::bulkGenerate).\nCreates <count> coupons sharing a discount configuration, each with a\nunique `<prefix><random>` code, and returns the created codes. `count` is\ncapped at 500","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"prefix":{"type":"string","maxLength":20},"count":{"type":"integer","minimum":1,"maximum":500},"discount_type":{"$ref":"#/components/schemas/CouponDiscountType"},"amount":{"type":"integer","minimum":0,"maximum":100000000},"max_discount_minor":{"type":["integer","null"],"minimum":0},"scope":{"$ref":"#/components/schemas/CouponScope"},"min_subtotal_minor":{"type":["integer","null"],"minimum":0},"valid_from":{"type":["string","null"],"format":"date-time"},"valid_to":{"type":["string","null"],"format":"date-time"},"max_redemptions":{"type":["integer","null"],"minimum":1},"per_customer_limit":{"type":["integer","null"],"minimum":1},"product_ids":{"type":["array","null"],"items":{"type":"string"}}},"required":["prefix","count","discount_type","amount","scope"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"},"codes":{"type":"array","items":{"type":"string"}}},"required":["count","codes"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/coupons/{coupon}":{"patch":{"operationId":"coupon.update","summary":"Toggle or edit a coupon","tags":["Marketing & Campaigns"],"parameters":[{"name":"coupon","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"discount_type":{"$ref":"#/components/schemas/CouponDiscountType"},"amount":{"type":"integer","minimum":0,"maximum":100000000},"max_discount_minor":{"type":["integer","null"],"minimum":0},"scope":{"$ref":"#/components/schemas/CouponScope"},"min_subtotal_minor":{"type":["integer","null"],"minimum":0},"valid_from":{"type":["string","null"],"format":"date-time"},"valid_to":{"type":["string","null"],"format":"date-time"},"max_redemptions":{"type":["integer","null"],"minimum":1},"per_customer_limit":{"type":["integer","null"],"minimum":1},"is_active":{"type":"boolean"},"stackable":{"type":"boolean","description":"RFP 1.7 promo-code rules: stack with other stackable codes + restrict to a channel."},"channel":{"type":["string","null"],"enum":["email","sms","push","pos","online"]},"product_ids":{"type":["array","null"],"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"discount_type":{"type":"string"},"amount":{"type":"integer"},"max_discount_minor":{"type":["integer","null"]},"scope":{"type":"string"},"product_ids":{"type":["array","null"],"items":{}},"min_subtotal_minor":{"type":["integer","null"]},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"max_redemptions":{"type":["integer","null"]},"redemptions_used":{"type":"integer"},"per_customer_limit":{"type":["integer","null"]},"is_active":{"type":"boolean"}},"required":["id","code","name","name_ar","discount_type","amount","max_discount_minor","scope","product_ids","min_subtotal_minor","valid_from","valid_to","max_redemptions","redemptions_used","per_customer_limit","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/coupons/{coupon}/redemptions":{"get":{"operationId":"coupon.redemptions","summary":"The redemption ledger + counts for one coupon","tags":["Marketing & Campaigns"],"parameters":[{"name":"coupon","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"coupon_id":{"type":"string"},"code":{"type":"string"},"redemptions_used":{"type":"string"},"max_redemptions":{"type":"string"},"total_discount_minor":{"type":"integer"},"redemptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":["string","null"]},"retail_sale_id":{"type":["string","null"]},"discount_minor":{"type":"integer"},"redeemed_at":{"type":"string"}},"required":["id","member_id","retail_sale_id","discount_minor","redeemed_at"]}}},"required":["coupon_id","code","redemptions_used","max_redemptions","total_discount_minor","redemptions"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/coupons/validate":{"post":{"operationId":"coupon.validateCode","summary":"Validate a coupon code against a cart, returning the discount + base.\nBody: {code, member_id?, items:[{product_id,qty,unit_price_minor}], subtotal_minor}","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"member_id":{"type":["string","null"]},"subtotal_minor":{"type":"integer","minimum":0},"items":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":"string"},"qty":{"type":"integer","minimum":1},"unit_price_minor":{"type":"integer","minimum":0}},"required":["product_id","qty","unit_price_minor"]}}},"required":["code","subtotal_minor","items"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"valid":{"type":"boolean"},"discount_minor":{"type":"string"},"base_minor":{"type":"string"},"coupon_id":{"type":"string"}},"required":["valid","discount_minor","base_minor","coupon_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/coupons/{code}/redeem":{"post":{"operationId":"coupon.redeem","summary":"Record a redemption (re-validates the global cap under a lock).\nBody: {member_id?, retail_sale_id?, discount_minor}","tags":["Marketing & Campaigns"],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":["string","null"]},"retail_sale_id":{"type":["string","null"]},"discount_minor":{"type":"integer","minimum":0},"gross_minor":{"type":["integer","null"],"minimum":0}},"required":["discount_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"redemption_id":{"type":"string"},"redemptions_used":{"type":"integer"}},"required":["redemption_id","redemptions_used"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/overview":{"get":{"operationId":"campaign.overview","summary":"GET /marketing/overview \u2014 the Marketing hub's aggregate: campaign funnel (sends/opens/clicks/\nconversions + attributed revenue) across the brand, a weekly send/conversion trend, and live tool\ncounts for the launcher grid. Reuses the same recipient + attribution data as per-campaign analytics","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"kpis":{"type":"object","properties":{"campaigns_total":{"type":"integer","minimum":0},"campaigns_sent":{"type":"integer","minimum":0},"campaigns_draft":{"type":"integer","minimum":0},"reach":{"type":"integer","minimum":0},"sent":{"type":"integer","minimum":0},"opened":{"type":"integer","minimum":0},"open_rate":{"type":"number"},"clicked":{"type":"integer","minimum":0},"click_rate":{"type":"number"},"conversions":{"type":"integer","minimum":0},"conversion_rate":{"type":"number"},"revenue_minor":{"type":"integer"},"redemptions":{"type":"integer","minimum":0}},"required":["campaigns_total","campaigns_sent","campaigns_draft","reach","sent","opened","open_rate","clicked","click_rate","conversions","conversion_rate","revenue_minor","redemptions"]},"trend":{"type":"array","items":{}},"tools":{"type":"object","properties":{"campaigns":{"type":"integer","minimum":0},"segments":{"type":"integer","minimum":0},"coupons":{"type":"integer","minimum":0},"promotions":{"type":"integer","minimum":0},"automations":{"type":"integer","minimum":0},"email_templates":{"type":"integer","minimum":0},"challenges":{"type":"integer","minimum":0}},"required":["campaigns","segments","coupons","promotions","automations","email_templates","challenges"]}},"required":["kpis","trend","tools"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns":{"get":{"operationId":"campaign.index","description":"This used to return every campaign (each carrying its full template body) in one response, and\nthe console filtered client-side. That's fine at 20 campaigns and painful at 500. Filtering\nserver-side also means a search spans ALL campaigns, not just the ones already downloaded.\n`stats` is computed across the whole brand so the headline tiles stay correct while paging.","summary":"List this brand's campaigns, newest first \u2014 PAGINATED, with search + status applied in SQL","tags":["Marketing & Campaigns"],"parameters":[{"name":"search","in":"query","schema":{"type":["string","null"],"maxLength":120}},{"name":"status","in":"query","schema":{"type":["string","null"],"enum":["all","draft","sent","cancelled"]}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"campaigns":{"type":"array","items":{"type":"object","properties":{"delivery":{"type":"object","description":"Live delivery progress \u2014 drives the \"sending\u2026 / stop\" affordance while jobs drain.","properties":{"queued":{"type":"integer"},"sent":{"type":"integer"},"failed":{"type":"integer"},"cancelled":{"type":"integer"}},"required":["queued","sent","failed","cancelled"]},"id":{"type":"string"},"name":{"type":"string"},"channel":{"type":"string"},"channels":{"type":"array","items":{}},"segment_id":{"type":["string","null"]},"audience":{"type":"array","items":{}},"coupon_id":{"type":["string","null"]},"template":{"type":"string"},"subject":{"type":["string","null"]},"status":{"type":"string"},"audience_count":{"type":"integer"},"cost_minor":{"type":"integer"},"sent_at":{"type":"string"}},"required":["delivery","id","name","channel","channels","segment_id","audience","coupon_id","template","subject","status","audience_count","cost_minor","sent_at"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"last_page":{"type":"integer"}},"required":["page","per_page","total","last_page"]},"stats":{"type":"object","properties":{"total":{"type":"integer","minimum":0},"sent":{"type":"integer","minimum":0},"drafts":{"type":"integer","minimum":0},"cancelled":{"type":"integer","minimum":0},"reached":{"type":"integer"}},"required":["total","sent","drafts","cancelled","reached"]}},"required":["campaigns","meta","stats"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"campaign.store","summary":"Create a draft campaign (audience is a declarative segment, not a recipient list)","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"channel":{"$ref":"#/components/schemas/CampaignChannel"},"segment_id":{"type":["string","null"],"description":"Either target a saved segment OR an inline audience \u2014 so audience is optional\n(a segment-based campaign legitimately omits it; the model defaults it to [])."},"coupon_id":{"type":["string","null"]},"template":{"type":"string"},"subject":{"type":["string","null"],"maxLength":160},"cost_minor":{"type":["integer","null"],"minimum":0},"channels":{"type":"array","items":{"$ref":"#/components/schemas/CampaignChannel"}},"audience":{"type":"object","properties":{"has_active_membership":{"type":"boolean"},"gender":{"$ref":"#/components/schemas/Gender"},"pipeline_stage":{"type":"string"},"segment":{"type":"string","enum":["at_risk","lapsed"]}}}},"required":["name","channel","template"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"delivery":{"type":"object","description":"Live delivery progress \u2014 drives the \"sending\u2026 / stop\" affordance while jobs drain.","properties":{"queued":{"type":"integer"},"sent":{"type":"integer"},"failed":{"type":"integer"},"cancelled":{"type":"integer"}},"required":["queued","sent","failed","cancelled"]},"id":{"type":"string"},"name":{"type":"string"},"channel":{"type":"string"},"channels":{"type":"array","items":{}},"segment_id":{"type":["string","null"]},"audience":{"type":"array","items":{}},"coupon_id":{"type":["string","null"]},"template":{"type":"string"},"subject":{"type":["string","null"]},"status":{"type":"string"},"audience_count":{"type":"integer"},"cost_minor":{"type":"integer"},"sent_at":{"type":"string"}},"required":["delivery","id","name","channel","channels","segment_id","audience","coupon_id","template","subject","status","audience_count","cost_minor","sent_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/email-templates":{"get":{"operationId":"emailTemplate.index_0","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"subject":{"type":"string"},"subject_ar":{"type":["string","null"]},"body":{"type":"string"},"body_ar":{"type":["string","null"]},"cta_label":{"type":["string","null"]},"cta_label_ar":{"type":["string","null"]},"cta_url":{"type":["string","null"]}},"required":["id","name","subject","subject_ar","body","body_ar","cta_label","cta_label_ar","cta_url"]}}},"required":["templates"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/winback/preview":{"get":{"operationId":"campaign.winbackPreview","summary":"Win-back preview (RFP 06): how many members fall in the at-risk / lapsed\nsegment and a small sample, so the UI can confirm the blast before sending.\nRead-only \u2014 creates nothing","tags":["Marketing & Campaigns"],"parameters":[{"name":"audience","in":"query","required":true,"schema":{"type":"string","enum":["at_risk","lapsed"]}},{"name":"limit","in":"query","description":"The card shows a short teaser; \"view all\" asks for the full list.","schema":{"type":"integer","minimum":1,"maximum":500}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"recipient_count":{"type":"integer","minimum":0},"returned":{"type":["object","null"]},"truncated":{"type":"boolean"},"sample":{"type":"array","items":{"type":"object","properties":{"member_id":{"type":"string"},"name":{"type":"string"},"days_dormant":{"type":["integer","null"]}},"required":["member_id","name","days_dormant"]}}},"required":["recipient_count","returned","truncated","sample"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/winback":{"post":{"operationId":"campaign.winback","summary":"Launch a win-back campaign (RFP 06): create a Campaign targeting the at-risk\nor lapsed segment with a personalised coupon offer, snapshot the recipients,\nmark it sent, and return the offer for the channel send. The coupon lives in\nFINANCE; we resolve it here in the App layer (identity never imports Coupon)","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"coupon_id":{"type":"string"},"audience":{"type":"string","enum":["at_risk","lapsed"]},"channel":{"$ref":"#/components/schemas/CampaignChannel"}},"required":["coupon_id","audience","channel"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"campaign_id":{"type":"string"},"recipient_count":{"type":"integer","minimum":0},"offer":{"type":["object","null"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"discount":{"type":"string"}},"required":["code","name","discount"]}},"required":["campaign_id","recipient_count","offer"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/{campaign}/audience":{"get":{"operationId":"campaign.audience","summary":"Preview the resolved audience: total count + first 10 member names","tags":["Marketing & Campaigns"],"parameters":[{"name":"campaign","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"preview":{"type":"array","items":{"type":"string"}}},"required":["count","preview"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/{campaign}":{"get":{"operationId":"campaign.show","description":"The console previously had no single-resource GET: opening one campaign downloaded the entire\nlist and picked it out client-side \u2014 and then re-polled that whole list every few seconds to\nwatch a send drain. This makes the detail page O(1), and is what allows the index to paginate\nat all (paginating without this would break campaign detail).","summary":"A single campaign + its live delivery counts","tags":["Marketing & Campaigns"],"parameters":[{"name":"campaign","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"delivery":{"type":"object","description":"Live delivery progress \u2014 drives the \"sending\u2026 / stop\" affordance while jobs drain.","properties":{"queued":{"type":"integer"},"sent":{"type":"integer"},"failed":{"type":"integer"},"cancelled":{"type":"integer"}},"required":["queued","sent","failed","cancelled"]},"id":{"type":"string"},"name":{"type":"string"},"channel":{"type":"string"},"channels":{"type":"array","items":{}},"segment_id":{"type":["string","null"]},"audience":{"type":"array","items":{}},"coupon_id":{"type":["string","null"]},"template":{"type":"string"},"subject":{"type":["string","null"]},"status":{"type":"string"},"audience_count":{"type":"integer"},"cost_minor":{"type":"integer"},"sent_at":{"type":"string"}},"required":["delivery","id","name","channel","channels","segment_id","audience","coupon_id","template","subject","status","audience_count","cost_minor","sent_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/{campaign}/send":{"post":{"operationId":"campaign.send","summary":"Deploy the campaign FOR REAL (RFP 1.7): resolve the audience, create a recipient per\n(member \u00d7 channel), dispatch email via Resend (with open/click tracking) + record SMS/push,\nthen mark it sent. Idempotent \u2014 re-sending a sent campaign is a no-op","tags":["Marketing & Campaigns"],"parameters":[{"name":"campaign","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"audience_count":{"type":"string"},"recipients":{"type":"string"},"channels":{"type":"string"},"status":{"type":"string"}},"required":["audience_count","recipients","channels","status"]},{"type":"object","properties":{"audience_count":{"type":"string"},"status":{"type":"string"},"already_sent":{"type":"boolean"}},"required":["audience_count","status","already_sent"]}]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/{campaign}/resend":{"post":{"operationId":"campaign.resend","description":"Re-queues the EXISTING recipient rows rather than creating new ones, so the funnel isn't\ndouble-counted; delivery stamps `resent_at` and leaves the original `sent_at` (and therefore the\nattribution window) intact. Only email is re-sendable \u2014 SMS/push have no transport wired.\nPassing `member_ids` targets specific people; omitting it nudges the whole opened-not-clicked set.","summary":"Re-send a campaign to recipients who OPENED but never CLICKED \u2014 the warmest cohort to nudge","tags":["Marketing & Campaigns"],"parameters":[{"name":"campaign","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"member_ids":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"resent":{"type":"integer","minimum":0},"members":{"type":"array","items":{}}},"required":["resent","members"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/{campaign}/cancel":{"post":{"operationId":"campaign.cancel","summary":"STOP a campaign that is still going out (RFP 1.7). Delivery is queued per recipient, so this\nmarks the campaign cancelled and drops everything not yet delivered \u2014 each pending job re-checks\nthe campaign before sending. Mail already handed to the provider cannot be recalled; the response\nreports what was actually stopped vs already gone","tags":["Marketing & Campaigns"],"parameters":[{"name":"campaign","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"stopped":{"type":"string"},"already_sent":{"type":"integer","minimum":0}},"required":["status","stopped","already_sent"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"This campaign has finished sending \u2014 there is nothing left to stop."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/{campaign}/analytics":{"get":{"operationId":"campaign.analytics","summary":"Post-campaign analytics: sends, opens, clicks, redemptions, conversions, revenue, ROI (per channel)","tags":["Marketing & Campaigns"],"parameters":[{"name":"campaign","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"integer","minimum":0},"opened":{"type":"integer","minimum":0},"open_rate":{"type":"number"},"clicked":{"type":"integer","minimum":0},"click_rate":{"type":"number"},"redemptions":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"conversion_rate":{"type":"number"},"revenue_minor":{"type":"integer"},"cost_minor":{"type":"integer"},"roi_percent":{"type":["number","null"]},"by_channel":{"type":"string"}},"required":["sent","opened","open_rate","clicked","click_rate","redemptions","conversions","conversion_rate","revenue_minor","cost_minor","roi_percent","by_channel"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/{campaign}/recipients":{"get":{"operationId":"campaign.recipients","summary":"Member-level drill-down (RFP 1.7): who was sent / opened / clicked / converted (redeemed) / lapsed\n(targeted but did not convert). One row per member with their per-channel engagement","tags":["Marketing & Campaigns"],"parameters":[{"name":"campaign","in":"path","required":true,"schema":{"type":"string"}},{"name":"filter","in":"query","schema":{"type":["string","null"]}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{}},"meta":{"type":"object","properties":{"page":{"type":["object","null"]},"per_page":{"type":"integer"},"total":{"type":"integer"},"last_page":{"type":["object","null"]}},"required":["page","per_page","total","last_page"]},"resendable":{"type":"integer","description":"The bulk-resend button needs the count across the WHOLE campaign, not this page."}},"required":["rows","meta","resendable"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/marketing/campaigns/{campaign}/recipients/export":{"get":{"operationId":"campaign.recipientsExport","summary":"CSV export of the member-level drill-down","tags":["Marketing & Campaigns"],"parameters":[{"name":"campaign","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"text/csv":{"schema":{"type":"string"}}},"headers":{"Transfer-Encoding":{"required":true,"schema":{"type":"string","enum":["chunked"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sms-gateways":{"get":{"operationId":"smsGateway.index","summary":"GET /sms-gateways \u2014 the brand's profiles + the provider catalogue","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"label":{"type":"string"},"username":{"type":["string","null"]},"sender_id":{"type":["string","null"]},"has_key":{"type":"boolean"},"is_active":{"type":"boolean"},"is_primary":{"type":"boolean"}},"required":["id","provider","label","username","sender_id","has_key","is_active","is_primary"]}},"catalog":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string"}},"required":["key","label"]}}},"required":["providers","catalog"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"smsGateway.upsert","summary":"POST /sms-gateways \u2014 create or update a profile (api_key only overwritten when provided)","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":["string","null"]},"provider":{"type":"string","enum":["msegat"]},"label":{"type":"string","maxLength":80},"username":{"type":["string","null"],"maxLength":160},"sender_id":{"type":["string","null"],"maxLength":32},"api_key":{"type":["string","null"],"maxLength":255},"is_active":{"type":"boolean"},"is_primary":{"type":"boolean"}},"required":["provider","label"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"label":{"type":"string"},"username":{"type":["string","null"]},"sender_id":{"type":["string","null"]},"has_key":{"type":"boolean"},"is_active":{"type":"boolean"},"is_primary":{"type":"boolean"}},"required":["id","provider","label","username","sender_id","has_key","is_active","is_primary"]}},"required":["provider"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sms-gateways/notifications":{"get":{"operationId":"smsGateway.notifications","summary":"GET /sms-gateways/notifications \u2014 the member notification catalogue + which types also SMS","tags":["Marketing & Campaigns"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","prefixItems":[{"type":"object","properties":{"group":{"type":"object","properties":{"en":{"type":"string","const":"Membership & account"},"ar":{"type":"string","const":"\u0627\u0644\u0639\u0636\u0648\u064a\u0629 \u0648\u0627\u0644\u062d\u0633\u0627\u0628"}},"required":["en","ar"]},"items":{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"membership_frozen"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Membership frozen"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062c\u0645\u064a\u062f \u0627\u0644\u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Membership frozen"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062c\u0645\u064a\u062f \u0627\u0644\u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your membership is frozen for {days} day(s)."},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062c\u0645\u064a\u062f \u0639\u0636\u0648\u064a\u062a\u0643 \u0644\u0645\u062f\u0629 {days} \u064a\u0648\u0645."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"membership_unfrozen"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Membership unfrozen"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0644\u063a\u0627\u0621 \u062a\u062c\u0645\u064a\u062f \u0627\u0644\u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Membership resumed"},"ar":{"type":"string","const":"\u062a\u0645 \u0627\u0633\u062a\u0626\u0646\u0627\u0641 \u0627\u0644\u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your membership is active again \u2014 welcome back!"},"ar":{"type":"string","const":"\u0639\u0627\u062f\u062a \u0639\u0636\u0648\u064a\u062a\u0643 \u0644\u0644\u0646\u0634\u0627\u0637 \u2014 \u0623\u0647\u0644\u0627\u064b \u0628\u0639\u0648\u062f\u062a\u0643!"}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"membership_renewed"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Membership renewed"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062c\u062f\u064a\u062f \u0627\u0644\u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Membership renewed"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062c\u062f\u064a\u062f \u0627\u0644\u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your membership has been renewed."},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062c\u062f\u064a\u062f \u0639\u0636\u0648\u064a\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"plan_changed"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Plan changed"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u063a\u064a\u064a\u0631 \u0627\u0644\u0628\u0627\u0642\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Plan changed"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u063a\u064a\u064a\u0631 \u0627\u0644\u0628\u0627\u0642\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your membership plan has been updated."},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062d\u062f\u064a\u062b \u0628\u0627\u0642\u0629 \u0639\u0636\u0648\u064a\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"reactivated"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Membership reactivated"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0639\u0627\u062f\u0629 \u062a\u0641\u0639\u064a\u0644 \u0627\u0644\u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Membership reactivated"},"ar":{"type":"string","const":"\u062a\u0645\u062a \u0625\u0639\u0627\u062f\u0629 \u062a\u0641\u0639\u064a\u0644 \u0627\u0644\u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your membership has been reactivated."},"ar":{"type":"string","const":"\u062a\u0645\u062a \u0625\u0639\u0627\u062f\u0629 \u062a\u0641\u0639\u064a\u0644 \u0639\u0636\u0648\u064a\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"change_pending"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Change request pending"},"ar":{"type":"string","const":"\u0637\u0644\u0628 \u062a\u063a\u064a\u064a\u0631 \u0642\u064a\u062f \u0627\u0644\u0645\u0631\u0627\u062c\u0639\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Request submitted"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0631\u0633\u0627\u0644 \u0627\u0644\u0637\u0644\u0628"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your request was submitted for staff review."},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0631\u0633\u0627\u0644 \u0637\u0644\u0628\u0643 \u0644\u0645\u0631\u0627\u062c\u0639\u0629 \u0627\u0644\u0645\u0648\u0638\u0641\u064a\u0646."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"access_revoked"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Access revoked"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u064a\u0642\u0627\u0641 \u0627\u0644\u062f\u062e\u0648\u0644"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Access revoked"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u064a\u0642\u0627\u0641 \u0627\u0644\u062f\u062e\u0648\u0644"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your gym access has been suspended. Please contact the front desk."},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u064a\u0642\u0627\u0641 \u062f\u062e\u0648\u0644\u0643 \u0644\u0644\u0646\u0627\u062f\u064a. \u064a\u0631\u062c\u0649 \u0627\u0644\u062a\u0648\u0627\u0635\u0644 \u0645\u0639 \u0627\u0644\u0627\u0633\u062a\u0642\u0628\u0627\u0644."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"agreement_ready"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Membership agreement ready"},"ar":{"type":"string","const":"\u0639\u0642\u062f \u0627\u0644\u0639\u0636\u0648\u064a\u0629 \u062c\u0627\u0647\u0632"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Your membership agreement is ready"},"ar":{"type":"string","const":"\u0627\u062a\u0641\u0627\u0642\u064a\u0629 \u0639\u0636\u0648\u064a\u062a\u0643 \u062c\u0627\u0647\u0632\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your signed membership agreement is available to view and download in the app."},"ar":{"type":"string","const":"\u0627\u062a\u0641\u0627\u0642\u064a\u0629 \u0639\u0636\u0648\u064a\u062a\u0643 \u0645\u062a\u0627\u062d\u0629 \u0644\u0644\u0639\u0631\u0636 \u0648\u0627\u0644\u062a\u0646\u0632\u064a\u0644 \u0641\u064a \u0627\u0644\u062a\u0637\u0628\u064a\u0642."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"waitlist_invited"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Membership spot available"},"ar":{"type":"string","const":"\u062a\u0648\u0641\u0631 \u0645\u0642\u0639\u062f \u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"A membership spot is available"},"ar":{"type":"string","const":"\u062a\u062a\u0648\u0641\u0631 \u0639\u0636\u0648\u064a\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"A spot has opened up \u2014 complete your payment within 24 hours to secure your membership."},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u0648\u0641\u0631 \u0645\u0642\u0639\u062f \u2014 \u0623\u0643\u0645\u0644 \u0627\u0644\u062f\u0641\u0639 \u062e\u0644\u0627\u0644 24 \u0633\u0627\u0639\u0629 \u0644\u062a\u0623\u0643\u064a\u062f \u0639\u0636\u0648\u064a\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]}],"minItems":9,"maxItems":9,"additionalItems":false}},"required":["group","items"]},{"type":"object","properties":{"group":{"type":"object","properties":{"en":{"type":"string","const":"Billing & payments"},"ar":{"type":"string","const":"\u0627\u0644\u0641\u0648\u062a\u0631\u0629 \u0648\u0627\u0644\u0645\u062f\u0641\u0648\u0639\u0627\u062a"}},"required":["en","ar"]},"items":{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"renewal_failed"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Renewal payment failed"},"ar":{"type":"string","const":"\u0641\u0634\u0644 \u062f\u0641\u0639 \u0627\u0644\u062a\u062c\u062f\u064a\u062f"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Payment failed \u2014 please update your card"},"ar":{"type":"string","const":"\u0641\u0634\u0644 \u0627\u0644\u062f\u0641\u0639 \u2014 \u064a\u0631\u062c\u0649 \u062a\u062d\u062f\u064a\u062b \u0628\u0637\u0627\u0642\u062a\u0643"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"We couldn't process your renewal payment. Please update your card to keep your membership active."},"ar":{"type":"string","const":"\u062a\u0639\u0630\u0651\u0631 \u062a\u0646\u0641\u064a\u0630 \u062f\u0641\u0639\u0629 \u0627\u0644\u062a\u062c\u062f\u064a\u062f. \u064a\u0631\u062c\u0649 \u062a\u062d\u062f\u064a\u062b \u0628\u0637\u0627\u0642\u062a\u0643 \u0644\u0644\u062d\u0641\u0627\u0638 \u0639\u0644\u0649 \u0639\u0636\u0648\u064a\u062a\u0643 \u0641\u0639\u0651\u0627\u0644\u0629."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"refund_issued"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Refund issued"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0635\u062f\u0627\u0631 \u0627\u0633\u062a\u0631\u062f\u0627\u062f"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Refund issued"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0635\u062f\u0627\u0631 \u0627\u0644\u0627\u0633\u062a\u0631\u062f\u0627\u062f"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your cooling-off refund has been approved and issued."},"ar":{"type":"string","const":"\u062a\u0645\u062a \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0629 \u0639\u0644\u0649 \u0627\u0633\u062a\u0631\u062f\u0627\u062f\u0643 \u0648\u0625\u0635\u062f\u0627\u0631\u0647."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"locker_rented"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Locker rented"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u0623\u062c\u064a\u0631 \u062e\u0632\u0627\u0646\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Locker rented"},"ar":{"type":"string","const":"\u062a\u0645 \u0627\u0633\u062a\u0626\u062c\u0627\u0631 \u062e\u0632\u0627\u0646\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your locker rental is confirmed."},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u0623\u0643\u064a\u062f \u0627\u0633\u062a\u0626\u062c\u0627\u0631 \u062e\u0632\u0627\u0646\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]}],"minItems":3,"maxItems":3,"additionalItems":false}},"required":["group","items"]},{"type":"object","properties":{"group":{"type":"object","properties":{"en":{"type":"string","const":"Classes & bookings"},"ar":{"type":"string","const":"\u0627\u0644\u062d\u0635\u0635 \u0648\u0627\u0644\u062d\u062c\u0648\u0632\u0627\u062a"}},"required":["en","ar"]},"items":{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"booking_confirmed"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Booking confirmed"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u0623\u0643\u064a\u062f \u0627\u0644\u062d\u062c\u0632"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Class booked"},"ar":{"type":"string","const":"\u062a\u0645 \u062d\u062c\u0632 \u0627\u0644\u062d\u0635\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your class booking is confirmed."},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u0623\u0643\u064a\u062f \u062d\u062c\u0632 \u062d\u0635\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"booking_cancelled"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Booking cancelled"},"ar":{"type":"string","const":"\u0625\u0644\u063a\u0627\u0621 \u0627\u0644\u062d\u062c\u0632"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Booking cancelled"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0644\u063a\u0627\u0621 \u0627\u0644\u062d\u062c\u0632"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your booking for {class} was cancelled."},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0644\u063a\u0627\u0621 \u062d\u062c\u0632\u0643 \u0644\u0640 {class}."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"booking_rescheduled"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Booking rescheduled"},"ar":{"type":"string","const":"\u062a\u063a\u064a\u064a\u0631 \u0645\u0648\u0639\u062f \u0627\u0644\u062d\u062c\u0632"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Booking rescheduled"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u063a\u064a\u064a\u0631 \u0645\u0648\u0639\u062f \u0627\u0644\u062d\u062c\u0632"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"You're now booked for {class} {when}."},"ar":{"type":"string","const":"\u062a\u0645 \u0646\u0642\u0644 \u062d\u062c\u0632\u0643 \u0625\u0644\u0649 {class} {when}."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"waitlist_joined"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Added to waitlist"},"ar":{"type":"string","const":"\u062a\u0645\u062a \u0627\u0644\u0625\u0636\u0627\u0641\u0629 \u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Added to the waitlist"},"ar":{"type":"string","const":"\u062a\u0645\u062a \u0625\u0636\u0627\u0641\u062a\u0643 \u0644\u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"You're on the waitlist for {class} \u2014 we'll notify you if a spot opens."},"ar":{"type":"string","const":"\u0623\u0646\u062a \u0639\u0644\u0649 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631 \u0644\u062d\u0635\u0629 {class} \u2014 \u0633\u0646\u0639\u0644\u0645\u0643 \u0639\u0646\u062f \u062a\u0648\u0641\u0631 \u0645\u0642\u0639\u062f."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"waitlist_offered"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Waitlist spot offered"},"ar":{"type":"string","const":"\u0639\u0631\u0636 \u0645\u0642\u0639\u062f \u0645\u0646 \u0642\u0627\u0626\u0645\u0629 \u0627\u0644\u0627\u0646\u062a\u0638\u0627\u0631"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"A spot opened \u2014 claim it!"},"ar":{"type":"string","const":"\u062a\u0648\u0641\u0651\u0631 \u0645\u0643\u0627\u0646 \u2014 \u0627\u062d\u062c\u0632\u0647!"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"A place opened in {class}. Claim it before the offer expires."},"ar":{"type":"string","const":"\u062a\u0648\u0641\u0651\u0631 \u0645\u0642\u0639\u062f \u0641\u064a {class}. \u0627\u062d\u062c\u0632\u0647 \u0642\u0628\u0644 \u0627\u0646\u062a\u0647\u0627\u0621 \u0627\u0644\u0639\u0631\u0636."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"session_cancelled"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Session cancelled"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0644\u063a\u0627\u0621 \u0627\u0644\u062d\u0635\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Class cancelled"},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0644\u063a\u0627\u0621 \u0627\u0644\u062d\u0635\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your class {class} on {when} was cancelled."},"ar":{"type":"string","const":"\u062a\u0645 \u0625\u0644\u063a\u0627\u0621 \u062d\u0635\u0629 {class} \u0628\u062a\u0627\u0631\u064a\u062e {when}."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"session_moved"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Session rescheduled"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u063a\u064a\u064a\u0631 \u0645\u0648\u0639\u062f \u0627\u0644\u062d\u0635\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Class rescheduled"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u0639\u062f\u064a\u0644 \u0645\u0648\u0639\u062f \u0627\u0644\u062d\u0635\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your class {class} was moved to {when}."},"ar":{"type":"string","const":"\u062a\u0645 \u0646\u0642\u0644 \u062d\u0635\u0629 {class} \u0625\u0644\u0649 {when}."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"trainer_changed"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Trainer changed"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u063a\u064a\u064a\u0631 \u0627\u0644\u0645\u062f\u0631\u0628"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Instructor changed"},"ar":{"type":"string","const":"\u062a\u063a\u064a\u0651\u0631 \u0627\u0644\u0645\u062f\u0631\u0628"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"The instructor for {class} has changed."},"ar":{"type":"string","const":"\u062a\u063a\u064a\u0651\u0631 \u0645\u062f\u0631\u0628 \u062d\u0635\u0629 {class}."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"series_updated"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Class series updated"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062d\u062f\u064a\u062b \u0633\u0644\u0633\u0644\u0629 \u0627\u0644\u062d\u0635\u0635"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Class updated"},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062d\u062f\u064a\u062b \u0627\u0644\u062d\u0635\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your recurring class {class} has been updated."},"ar":{"type":"string","const":"\u062a\u0645 \u062a\u062d\u062f\u064a\u062b \u062d\u0635\u062a\u0643 \u0627\u0644\u0645\u062a\u0643\u0631\u0631\u0629 {class}."}},"required":["en","ar"]}},"required":["key","label","title","body"]}],"minItems":9,"maxItems":9,"additionalItems":false}},"required":["group","items"]},{"type":"object","properties":{"group":{"type":"object","properties":{"en":{"type":"string","const":"Personal training"},"ar":{"type":"string","const":"\u0627\u0644\u062a\u062f\u0631\u064a\u0628 \u0627\u0644\u0634\u062e\u0635\u064a"}},"required":["en","ar"]},"items":{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"program_assigned"},"label":{"type":"object","properties":{"en":{"type":"string","const":"New training program"},"ar":{"type":"string","const":"\u0628\u0631\u0646\u0627\u0645\u062c \u062a\u062f\u0631\u064a\u0628\u064a \u062c\u062f\u064a\u062f"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"New training program"},"ar":{"type":"string","const":"\u0628\u0631\u0646\u0627\u0645\u062c \u062a\u062f\u0631\u064a\u0628\u064a \u062c\u062f\u064a\u062f"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your coach assigned you a new training program."},"ar":{"type":"string","const":"\u0623\u0633\u0646\u062f \u0625\u0644\u064a\u0643 \u0645\u062f\u0631\u0628\u0643 \u0628\u0631\u0646\u0627\u0645\u062c\u064b\u0627 \u062a\u062f\u0631\u064a\u0628\u064a\u064b\u0627 \u062c\u062f\u064a\u062f\u064b\u0627."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"pt_expiring"},"label":{"type":"object","properties":{"en":{"type":"string","const":"PT package expiring"},"ar":{"type":"string","const":"\u0628\u0627\u0642\u0629 \u0627\u0644\u062a\u062f\u0631\u064a\u0628 \u0642\u0627\u0631\u0628\u062a \u0639\u0644\u0649 \u0627\u0644\u0627\u0646\u062a\u0647\u0627\u0621"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Your PT sessions are expiring soon"},"ar":{"type":"string","const":"\u062c\u0644\u0633\u0627\u062a \u0627\u0644\u062a\u062f\u0631\u064a\u0628 \u0627\u0644\u0634\u062e\u0635\u064a \u0642\u0627\u0631\u0628\u062a \u0639\u0644\u0649 \u0627\u0644\u0627\u0646\u062a\u0647\u0627\u0621"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"You have {count} PT session(s) left \u2014 they expire on {when}. Book before they lapse."},"ar":{"type":"string","const":"\u0644\u062f\u064a\u0643 {count} \u062c\u0644\u0633\u0629 \u062a\u062f\u0631\u064a\u0628 \u0645\u062a\u0628\u0642\u064a\u0629 \u2014 \u062a\u0646\u062a\u0647\u064a \u0641\u064a {when}. \u0627\u062d\u062c\u0632 \u0642\u0628\u0644 \u0627\u0646\u062a\u0647\u0627\u0626\u0647\u0627."}},"required":["en","ar"]}},"required":["key","label","title","body"]}],"minItems":2,"maxItems":2,"additionalItems":false}},"required":["group","items"]},{"type":"object","properties":{"group":{"type":"object","properties":{"en":{"type":"string","const":"Workouts & progress"},"ar":{"type":"string","const":"\u0627\u0644\u062a\u0645\u0627\u0631\u064a\u0646 \u0648\u0627\u0644\u062a\u0642\u062f\u0651\u0645"}},"required":["en","ar"]},"items":{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"workout_pr"},"label":{"type":"object","properties":{"en":{"type":"string","const":"New personal record"},"ar":{"type":"string","const":"\u0631\u0642\u0645 \u0634\u062e\u0635\u064a \u062c\u062f\u064a\u062f"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"New personal record \ud83d\udcaa"},"ar":{"type":"string","const":"\u0631\u0642\u0645 \u0634\u062e\u0635\u064a \u062c\u062f\u064a\u062f \ud83d\udcaa"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"{exercise} \u2014 new PR: {value} {unit}!"},"ar":{"type":"string","const":"{exercise} \u2014 \u0631\u0642\u0645 \u0642\u064a\u0627\u0633\u064a \u062c\u062f\u064a\u062f: {value} {unit}!"}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"goal_achieved"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Fitness goal achieved"},"ar":{"type":"string","const":"\u062a\u062d\u0642\u064a\u0642 \u0647\u062f\u0641 \u0627\u0644\u0644\u064a\u0627\u0642\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Goal achieved \ud83c\udfaf"},"ar":{"type":"string","const":"\u062a\u062d\u0642\u0651\u0642 \u0627\u0644\u0647\u062f\u0641 \ud83c\udfaf"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"You reached your goal: {goal}."},"ar":{"type":"string","const":"\u0648\u0635\u0644\u062a \u0625\u0644\u0649 \u0647\u062f\u0641\u0643: {goal}."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"routine_reviewed"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Coach reviewed your routine"},"ar":{"type":"string","const":"\u0631\u0627\u062c\u0639 \u0645\u062f\u0631\u0628\u0643 \u062a\u0645\u0631\u064a\u0646\u0643"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Your coach reviewed your workout"},"ar":{"type":"string","const":"\u0631\u0627\u062c\u0639 \u0645\u062f\u0631\u0628\u0643 \u062a\u0645\u0631\u064a\u0646\u0643"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your coach reviewed {routine} \u2014 open the app to see their notes."},"ar":{"type":"string","const":"\u0631\u0627\u062c\u0639 \u0645\u062f\u0631\u0628\u0643 {routine} \u2014 \u0627\u0641\u062a\u062d \u0627\u0644\u062a\u0637\u0628\u064a\u0642 \u0644\u0644\u0627\u0637\u0644\u0627\u0639 \u0639\u0644\u0649 \u0645\u0644\u0627\u062d\u0638\u0627\u062a\u0647."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"wod_published"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Today's WOD is ready"},"ar":{"type":"string","const":"\u062a\u0645\u0631\u064a\u0646 \u0627\u0644\u064a\u0648\u0645 \u062c\u0627\u0647\u0632"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Today's workout is ready"},"ar":{"type":"string","const":"\u062a\u0645\u0631\u064a\u0646 \u0627\u0644\u064a\u0648\u0645 \u062c\u0627\u0647\u0632"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"{wod} is live \u2014 come train today!"},"ar":{"type":"string","const":"{wod} \u0645\u062a\u0627\u062d \u0627\u0644\u0622\u0646 \u2014 \u062a\u0639\u0627\u0644 \u0648\u062a\u062f\u0631\u0651\u0628 \u0627\u0644\u064a\u0648\u0645!"}},"required":["en","ar"]}},"required":["key","label","title","body"]}],"minItems":4,"maxItems":4,"additionalItems":false}},"required":["group","items"]},{"type":"object","properties":{"group":{"type":"object","properties":{"en":{"type":"string","const":"Requests & compliance"},"ar":{"type":"string","const":"\u0627\u0644\u0637\u0644\u0628\u0627\u062a \u0648\u0627\u0644\u0627\u0645\u062a\u062b\u0627\u0644"}},"required":["en","ar"]},"items":{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"change_request_approved"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Change request approved"},"ar":{"type":"string","const":"\u062a\u0645\u062a \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0629 \u0639\u0644\u0649 \u0637\u0644\u0628 \u0627\u0644\u062a\u063a\u064a\u064a\u0631"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Change request approved"},"ar":{"type":"string","const":"\u062a\u0645\u062a \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0629 \u0639\u0644\u0649 \u0637\u0644\u0628 \u0627\u0644\u062a\u0639\u062f\u064a\u0644"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your profile change request was approved."},"ar":{"type":"string","const":"\u062a\u0645\u062a \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0629 \u0639\u0644\u0649 \u0637\u0644\u0628 \u062a\u0639\u062f\u064a\u0644 \u0628\u064a\u0627\u0646\u0627\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"change_request_rejected"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Change request rejected"},"ar":{"type":"string","const":"\u062a\u0645 \u0631\u0641\u0636 \u0637\u0644\u0628 \u0627\u0644\u062a\u063a\u064a\u064a\u0631"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Change request declined"},"ar":{"type":"string","const":"\u062a\u0645 \u0631\u0641\u0636 \u0637\u0644\u0628 \u0627\u0644\u062a\u0639\u062f\u064a\u0644"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Your profile change request was not approved."},"ar":{"type":"string","const":"\u0644\u0645 \u062a\u062a\u0645 \u0627\u0644\u0645\u0648\u0627\u0641\u0642\u0629 \u0639\u0644\u0649 \u0637\u0644\u0628 \u062a\u0639\u062f\u064a\u0644 \u0628\u064a\u0627\u0646\u0627\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]},{"type":"object","properties":{"key":{"type":"string","const":"health_waiver_reminder"},"label":{"type":"object","properties":{"en":{"type":"string","const":"Health waiver reminder"},"ar":{"type":"string","const":"\u062a\u0630\u0643\u064a\u0631 \u0628\u0625\u0642\u0631\u0627\u0631 \u0627\u0644\u062d\u0627\u0644\u0629 \u0627\u0644\u0635\u062d\u064a\u0629"}},"required":["en","ar"]},"title":{"type":"object","properties":{"en":{"type":"string","const":"Complete your health waiver"},"ar":{"type":"string","const":"\u0623\u0643\u0645\u0644 \u0625\u0642\u0631\u0627\u0631 \u0627\u0644\u0644\u064a\u0627\u0642\u0629 \u0627\u0644\u0635\u062d\u064a\u0629"}},"required":["en","ar"]},"body":{"type":"object","properties":{"en":{"type":"string","const":"Please complete your PAR-Q health waiver so your membership can be activated."},"ar":{"type":"string","const":"\u064a\u0631\u062c\u0649 \u0625\u0643\u0645\u0627\u0644 \u0625\u0642\u0631\u0627\u0631 \u0627\u0644\u0644\u064a\u0627\u0642\u0629 \u0627\u0644\u0635\u062d\u064a\u0629 (PAR-Q) \u062d\u062a\u0649 \u064a\u062a\u0645 \u062a\u0641\u0639\u064a\u0644 \u0639\u0636\u0648\u064a\u062a\u0643."}},"required":["en","ar"]}},"required":["key","label","title","body"]}],"minItems":3,"maxItems":3,"additionalItems":false}},"required":["group","items"]}],"minItems":6,"maxItems":6,"additionalItems":false},"enabled":{"type":"object","additionalProperties":{}}},"required":["groups","enabled"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"smsGateway.updateNotifications","summary":"PUT /sms-gateways/notifications {types:[...]} \u2014 set which notification types also send an SMS","tags":["Marketing & Campaigns"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"types":{"type":"array","items":{"type":"string","enum":["membership_frozen","membership_unfrozen","membership_renewed","plan_changed","reactivated","change_pending","access_revoked","agreement_ready","waitlist_invited","renewal_failed","refund_issued","locker_rented","booking_confirmed","booking_cancelled","booking_rescheduled","waitlist_joined","waitlist_offered","session_cancelled","session_moved","trainer_changed","series_updated","program_assigned","pt_expiring","workout_pr","goal_achieved","routine_reviewed","wod_published","change_request_approved","change_request_rejected","health_waiver_reminder"]}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"object","additionalProperties":{}}},"required":["enabled"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sms-gateways/test":{"post":{"operationId":"smsGateway.test","summary":"POST /sms-gateways/test \u2014 send a test SMS via a chosen profile (or the primary)","tags":["Marketing & Campaigns"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","maxLength":20},"provider_id":{"type":["string","null"]},"text":{"type":["string","null"],"maxLength":400}},"required":["to"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"ok":{"type":"boolean"},"code":{"type":"string"},"message":{"type":"string"}},"required":["ok","code","message"]},{"type":"object","properties":{"ok":{"type":"boolean"},"code":{"type":"string","const":"no_provider"},"message":{"type":"string","const":"No SMS provider configured."}},"required":["ok","code","message"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sms-gateways/{provider}/primary":{"post":{"operationId":"smsGateway.setPrimary","summary":"POST /sms-gateways/{provider}/primary \u2014 make this profile the one sends go through","tags":["Marketing & Campaigns"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"provider":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"label":{"type":"string"},"username":{"type":["string","null"]},"sender_id":{"type":["string","null"]},"has_key":{"type":"boolean"},"is_active":{"type":"boolean"},"is_primary":{"type":"boolean"}},"required":["id","provider","label","username","sender_id","has_key","is_active","is_primary"]}},"required":["provider"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sms-gateways/{provider}":{"delete":{"operationId":"smsGateway.destroy","summary":"DELETE /sms-gateways/{provider} \u2014 remove a profile","tags":["Marketing & Campaigns"],"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/member/login":{"post":{"operationId":"memberAuth.login","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"identifier":{"type":"string"},"password":{"type":"string"}},"required":["identifier","password"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/member/otp/request":{"post":{"operationId":"memberAuth.requestOtp","summary":"Request a one-time login code. Anti-enumeration: always 200 \u2014 we only generate + email a code\nwhen the identifier resolves to a member with an email. `debug_code` is returned only when\nMEMBER_OTP_DEBUG is on (demo accounts whose inbox the presenter can't open)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"identifier":{"type":"string"}},"required":["identifier"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"},"channel":{"type":"string","const":"email"},"sent_to":{"type":"string"},"debug_code":{"type":"string"}},"required":["sent","channel","sent_to","debug_code"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/member/otp/verify":{"post":{"operationId":"memberAuth.verifyOtp","summary":"Verify a one-time code and issue a member-app token","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"identifier":{"type":"string"},"code":{"type":"string"}},"required":["identifier","code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"member":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"first_name_ar":{"type":["string","null"]},"last_name_ar":{"type":["string","null"]},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"gender":{"type":"string"},"language_pref":{"type":"string"},"avatar_url":{"type":"null"}},"required":["id","first_name","last_name","first_name_ar","last_name_ar","email","mobile","gender","language_pref","avatar_url"]}},"required":["token","member"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/member/password/forgot":{"post":{"operationId":"memberAuth.forgotPassword","summary":"Forgot-password: send a one-time reset code (same code mechanism as OTP login, reset-flavoured\nemail). Anti-enumeration: always 200 regardless of whether the identifier resolves","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"identifier":{"type":"string"}},"required":["identifier"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"},"channel":{"type":"string","const":"email"},"sent_to":{"type":"string"},"debug_code":{"type":"string"}},"required":["sent","channel","sent_to","debug_code"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/member/password/reset":{"post":{"operationId":"memberAuth.resetPassword","summary":"Reset the password using the code from forgotPassword, then issue a token so the member is\nlogged straight in. Verifies the same cached one-time code as verifyOtp","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"identifier":{"type":"string"},"code":{"type":"string"},"password":{"type":"string","minLength":8}},"required":["identifier","code","password"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"member":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"first_name_ar":{"type":["string","null"]},"last_name_ar":{"type":["string","null"]},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"gender":{"type":"string"},"language_pref":{"type":"string"},"avatar_url":{"type":"null"}},"required":["id","first_name","last_name","first_name_ar","last_name_ar","email","mobile","gender","language_pref","avatar_url"]}},"required":["token","member"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/me/documents/{document}/download":{"get":{"operationId":"me.documents.download","summary":"GET /me/documents/{document}/download \u2014 stream a document. This route is NOT token-authenticated:\nit is authorised by a SIGNED url (see the `signed` middleware on the route), so a member can open\nthe download link directly in a browser without a bearer token. The signature \u2014 minted only by the\nauthenticated index() for the member's own documents \u2014 covers the document id + expiry, so links\ncan't be forged or enumerated. Brand scope is bypassed (a browser request carries no X-Brand)","tags":["Member App (self-service)"],"parameters":[{"name":"document","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"text/html":{"schema":{"type":"string"}}},"headers":{"Transfer-Encoding":{"required":true,"schema":{"type":"string","enum":["chunked"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/me/membership":{"get":{"operationId":"meMembership.show","summary":"GET /me/membership \u2014 the member's current membership + the actions they may take now","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"string"},{"type":"object","properties":{"has_membership":{"type":"boolean"},"membership":{"type":"null"}},"required":["has_membership","membership"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/freeze-log":{"get":{"operationId":"meMembership.freezeLog","summary":"GET /me/membership/freeze-log \u2014 the member's freeze summary + full history for the mobile app:\nthe free-day allowance and how much remains, how many freezes have been used vs. remaining, the\nCURRENT freeze window (start + end dates) if the membership is frozen right now, and every past\nfreeze with all its details. The member is resolved from the token, so it's always their own","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"has_membership":{"type":"boolean"},"membership":{"type":"object","properties":{"contract_id":{"type":"string"},"plan":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"state":{"type":"string"},"expiry_date":{"type":"string"},"paid_days_remaining":{"type":"integer"}},"required":["contract_id","plan","state","expiry_date","paid_days_remaining"]},"policy":{"type":["object","null"],"properties":{"free_days":{"type":"string"},"min_duration_days":{"type":"string"},"max_requests":{"type":"string"},"paid_fee_minor":{"type":"string"},"currency":{"type":"string"}},"required":["free_days","min_duration_days","max_requests","paid_fee_minor","currency"]},"usage":{"type":"object","properties":{"used_free_days":{"type":"integer"},"free_days_remaining":{"type":["object","null"]},"used_requests":{"type":"integer","minimum":0},"requests_remaining":{"type":["object","null"]},"total_freezes":{"type":"integer","minimum":0}},"required":["used_free_days","free_days_remaining","used_requests","requests_remaining","total_freezes"]},"is_frozen":{"type":"boolean"},"current_freeze":{"type":["object","null"],"properties":{"id":{"type":"string"},"starts_on":{"type":"string"},"ends_on":{"type":"string"},"requested_days":{"type":"integer"},"status":{"type":"string"},"is_paid":{"type":"boolean"},"fee_minor":{"type":"integer"},"currency":{"type":"string"},"back_dated":{"type":"boolean"},"reason_code":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","starts_on","ends_on","requested_days","status","is_paid","fee_minor","currency","back_dated","reason_code","created_at"]},"history":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"starts_on":{"type":"string"},"ends_on":{"type":"string"},"requested_days":{"type":"integer"},"status":{"type":"string"},"is_paid":{"type":"boolean"},"fee_minor":{"type":"integer"},"currency":{"type":"string"},"back_dated":{"type":"boolean"},"reason_code":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","starts_on","ends_on","requested_days","status","is_paid","fee_minor","currency","back_dated","reason_code","created_at"]}}},"required":["has_membership","membership","policy","usage","is_frozen","current_freeze","history"]},{"type":"object","properties":{"has_membership":{"type":"boolean"}},"required":["has_membership"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/freeze-quote":{"get":{"operationId":"meMembership.freezeQuote","summary":"GET /me/membership/freeze-quote?days=N&starts_on=? \u2014 quote an N-day freeze for the mobile app:\nwhether it's free or PAID (free days exhausted), the fee, and the wallet balance so the app can\noffer \"pay from wallet\". No side effects","tags":["Member App (self-service)"],"parameters":[{"name":"days","in":"query","required":true,"schema":{"type":"integer","minimum":1,"maximum":365}},{"name":"starts_on","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"string"},{"type":"object","properties":{"has_membership":{"type":"boolean"}},"required":["has_membership"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/purchase":{"post":{"operationId":"meMembership.purchase","description":"Body: { plan_id, club_id, add_on_ids?[], coupon_code?, gateway? }\nReturns 201 { contract_id, payment_id, amount_minor, discount_minor, currency, redirect_url }.","summary":"POST /me/membership/purchase \u2014 a registered member (e.g. a self-registered prospect with no\nmembership yet) buys their FIRST membership from the app. This is the token-authed twin of the\npublic join: it drafts a PENDING contract on the Mobile channel + a pending card charge, then\nhands off to the gateway. The membership ACTIVATES only when the payment settles (webhook), so\nthe member never gets an active plan before paying. Already-enrolled members use upgrade/renew","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan_id":{"type":"string"},"club_id":{"type":["string","null"],"description":"Optional: defaults to the member's home branch, else the brand's only branch (below)."},"coupon_code":{"type":["string","null"],"maxLength":60},"gateway":{"type":["string","null"]},"add_on_ids":{"type":["array","null"],"items":{"type":"string"}}},"required":["plan_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contract_id":{"type":"string"},"payment_id":{"type":"string"},"amount_minor":{"type":["object","null"]},"discount_minor":{"type":"integer"},"currency":{"type":"string"},"redirect_url":{"type":"string"}},"required":["contract_id","payment_id","amount_minor","discount_minor","currency","redirect_url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/freeze":{"post":{"operationId":"meMembership.freeze","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer","minimum":1,"maximum":365},"starts_on":{"type":["string","null"],"format":"date-time","description":"after_or_equal:today keeps members out of the back-dated path (GM-approval only)."},"reason_code":{"type":["string","null"],"maxLength":64},"method":{"type":["string","null"],"description":"How to pay a PAID freeze (over the free quota): instant wallet, or a hosted card/BNPL charge.","enum":["wallet","card"]},"gateway":{"type":["string","null"]}},"required":["days"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/unfreeze":{"post":{"operationId":"meMembership.unfreeze","summary":"POST /me/membership/unfreeze \u2014 lift the currently active freeze early","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contract_id":{"type":"string"},"plan":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"state":{"type":"string"},"activation_date":{"type":"string"},"expiry_date":{"type":"string"},"paid_days_remaining":{"type":"integer"},"is_frozen":{"type":"boolean"},"freeze":{"type":"object","properties":{"active":{"type":"boolean"},"ends_on":{"type":"string"},"free_days":{"type":"string"},"free_remaining":{"type":["object","null"]},"min_days":{"type":"string"},"paid_fee_minor":{"type":"string"},"currency":{"type":"string"}},"required":["active","ends_on","free_days","free_remaining","min_days","paid_fee_minor","currency"]},"cancellation_reason":{"type":["string","null"]},"available_actions":{"type":"object","properties":{"freeze":{"type":"string"},"unfreeze":{"type":"boolean"},"cancel":{"type":"string"},"withdraw_cancellation":{"type":"boolean"},"renew":{"type":"boolean"},"upgrade":{"type":"boolean"}},"required":["freeze","unfreeze","cancel","withdraw_cancellation","renew","upgrade"]}},"required":["contract_id","plan","state","activation_date","expiry_date","paid_days_remaining","is_frozen","freeze","cancellation_reason","available_actions"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},{"type":"object","properties":{"message":{"type":"string","const":"This membership is not currently frozen."}},"required":["message"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/cancel":{"post":{"operationId":"meMembership.cancel","summary":"POST /me/membership/cancel \u2014 request cancellation (\u2192 PendingCancellation, notice period)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason_code":{"type":"string","maxLength":64},"acknowledge_notice":{"type":"string","description":"The member must explicitly acknowledge the notice period (RFP 23 #11)."}},"required":["reason_code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"meMembership.withdrawCancellation","summary":"DELETE /me/membership/cancel \u2014 withdraw a pending cancellation during the notice window","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contract_id":{"type":"string"},"plan":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"state":{"type":"string"},"activation_date":{"type":"string"},"expiry_date":{"type":"string"},"paid_days_remaining":{"type":"integer"},"is_frozen":{"type":"boolean"},"freeze":{"type":"object","properties":{"active":{"type":"boolean"},"ends_on":{"type":"string"},"free_days":{"type":"string"},"free_remaining":{"type":["object","null"]},"min_days":{"type":"string"},"paid_fee_minor":{"type":"string"},"currency":{"type":"string"}},"required":["active","ends_on","free_days","free_remaining","min_days","paid_fee_minor","currency"]},"cancellation_reason":{"type":["string","null"]},"available_actions":{"type":"object","properties":{"freeze":{"type":"string"},"unfreeze":{"type":"boolean"},"cancel":{"type":"string"},"withdraw_cancellation":{"type":"boolean"},"renew":{"type":"boolean"},"upgrade":{"type":"boolean"}},"required":["freeze","unfreeze","cancel","withdraw_cancellation","renew","upgrade"]}},"required":["contract_id","plan","state","activation_date","expiry_date","paid_days_remaining","is_frozen","freeze","cancellation_reason","available_actions"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/renew":{"post":{"operationId":"meMembership.renew","summary":"POST /me/membership/renew \u2014 reactivate a recently lapsed membership within the grace\nwindow (free restore). Beyond the window we return requires_new_contract so the portal\ncan route the member to buy a fresh plan (a paid renewal lands with the checkout phase)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/requests":{"get":{"operationId":"meMembership.memberRequests","summary":"GET /me/membership/requests \u2014 the member's app-change requests + their approval status (#14)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"anyOf":[{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string"},"process":{"type":"string"},"status":{"type":"string"},"requested_at":{"type":"string"}},"required":["id","process","status","requested_at"]}},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"process":{"type":"string"},"status":{"type":"string"},"requested_at":{"type":"string"}},"required":["id","process","status","requested_at"]}}]}},"required":["requests"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/plan-options":{"get":{"operationId":"meMembership.planOptions","summary":"GET /me/membership/plan-options \u2014 sellable plans the member can switch to, each priced at the\nmember's channel/club with an is_upgrade flag (RFP 23 #13 \"target plan options\")","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"current_plan_id":{"type":"string"},"current_amount_minor":{"type":"integer"},"options":{"type":"array","items":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"amount_minor":{"type":"integer"},"duration_months":{"type":["integer","null"]},"duration_days":{"type":["integer","null"]},"billing_mode":{"type":"string"},"is_upgrade":{"type":"boolean"}},"required":["id","name","name_ar","amount_minor","duration_months","duration_days","billing_mode","is_upgrade"]}}},"required":["current_plan_id","current_amount_minor","options"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"Only an active membership can be changed."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/upgrade-preview":{"get":{"operationId":"meMembership.upgradePreview","summary":"GET /me/membership/upgrade-preview?target_plan_id= \u2014 read-only pro-rata preview (RFP 23 #13):\nthe unused value of the current plan credited against the target's cost for the remaining\nperiod, plus any out-of-window upgrade fee, the amount due now, and a signed token the confirm\nstep must echo back (prevents stale-price submission)","tags":["Member App (self-service)"],"parameters":[{"name":"target_plan_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"current_plan":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"amount_minor":{"type":"integer"}},"required":["id","name","name_ar","amount_minor"]},"target_plan":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"amount_minor":{"type":"integer"},"duration_months":{"type":["integer","null"]}},"required":["id","name","name_ar","amount_minor","duration_months"]},"is_upgrade":{"type":"boolean"},"remaining_days":{"type":"integer"},"credit_minor":{"type":"integer"},"target_prorata_minor":{"type":"integer"},"within_upgrade_window":{"type":"boolean"},"late_upgrade_fee_minor":{"type":"integer"},"amount_due_minor":{"anyOf":[{"type":"string"},{"type":"integer","enum":[0]}]},"credit_to_wallet_minor":{"anyOf":[{"type":"null"},{"type":"object"},{"type":"integer","enum":[0]}]},"new_billing_minor":{"type":"integer"},"expiry_date":{"type":"string"},"currency":{"type":"string"},"preview_token":{"type":"string"}},"required":["current_plan","target_plan","is_upgrade","remaining_days","credit_minor","target_prorata_minor","within_upgrade_window","late_upgrade_fee_minor","amount_due_minor","credit_to_wallet_minor","new_billing_minor","expiry_date","currency","preview_token"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/membership/upgrade":{"post":{"operationId":"meMembership.upgrade","summary":"POST /me/membership/upgrade \u2014 confirm the plan change (RFP 23 #14). Validates the preview token\n(anti stale-price), settles the difference via the wallet (upgrade debits, downgrade credits),\nthen changePlan re-prorates the remaining days, swaps the plan + access rights, and keeps the\noriginal start date","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"target_plan_id":{"type":"string"},"preview_token":{"type":"string"},"method":{"type":["string","null"],"description":"How to settle the difference: 'wallet' (instant, store credit) or 'card' (hosted page,\napplied only once it settles). The chosen gateway must be one the brand has enabled.","enum":["wallet","card"]},"gateway":{"type":["string","null"]},"coupon_code":{"type":["string","null"],"description":"Optional promo code applied to the difference (a cart-scope coupon).","maxLength":60}},"required":["target_plan_id","preview_token"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contract_id":{"type":"string"},"plan":{"anyOf":[{"type":"string"},{"type":"string","enum":[""]}]},"state":{"type":"string"},"activation_date":{"type":"string"},"expiry_date":{"type":"string"},"paid_days_remaining":{"type":"integer"},"is_frozen":{"type":"boolean"},"freeze":{"type":"object","properties":{"active":{"type":"boolean"},"ends_on":{"type":"string"},"free_days":{"type":"string"},"free_remaining":{"type":["object","null"]},"min_days":{"type":"string"},"paid_fee_minor":{"type":"string"},"currency":{"type":"string"}},"required":["active","ends_on","free_days","free_remaining","min_days","paid_fee_minor","currency"]},"cancellation_reason":{"type":["string","null"]},"available_actions":{"type":"object","properties":{"freeze":{"type":"string"},"unfreeze":{"type":"boolean"},"cancel":{"type":"string"},"withdraw_cancellation":{"type":"boolean"},"renew":{"type":"boolean"},"upgrade":{"type":"boolean"}},"required":["freeze","unfreeze","cancel","withdraw_cancellation","renew","upgrade"]}},"required":["contract_id","plan","state","activation_date","expiry_date","paid_days_remaining","is_frozen","freeze","cancellation_reason","available_actions"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"redirect_url":{"type":["string","null"]},"requires_payment":{"type":"boolean"},"discount_minor":{"type":"string"}},"required":["payment_id","redirect_url","requires_payment","discount_minor"]}}}},"202":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"pending_approval"},"approval_id":{"type":"string"},"message":{"type":"string","const":"Your upgrade request was submitted for staff review."}},"required":["status","approval_id","message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/credential/qr":{"get":{"operationId":"me.qr","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"qr_token":{"type":"string"},"expires_in":{"type":"integer"}},"required":["qr_token","expires_in"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/invoices":{"get":{"operationId":"me.invoices","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"seller_name":{"anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":"string"},"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"issued_at":{"type":"string"},"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"gross_minor":{"type":"integer"},"currency":{"type":"string"},"zatca_qr":{"type":["string","null"]}},"required":["id","number","issued_at","net_minor","vat_minor","gross_minor","currency","zatca_qr"]}}},"required":["seller_name","vat_number","invoices"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/payment-methods":{"get":{"operationId":"mePaymentMethod.index","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gateway":{"type":"string"},"scheme":{"type":["string","null"]},"last4":{"type":["string","null"]},"exp_month":{"type":["string","null"]},"exp_year":{"type":["string","null"]},"is_primary":{"type":"boolean"},"is_backup":{"type":"boolean"}},"required":["id","gateway","scheme","last4","exp_month","exp_year","is_primary","is_backup"]}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"mePaymentMethod.store","summary":"Add a card: start a small \"card verification\" save-card charge and return the gateway\nredirect. The card is vaulted automatically on settlement (PaymentController store-on-settle),\nso the member just completes the hosted-page flow and returns","tags":["Member App (self-service)"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"redirect_url":{"type":["string","null"]},"status":{"type":"string"}},"required":["payment_id","redirect_url","status"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["Saving cards isn't enabled on the payment account yet. Enable card tokenisation in the gateway dashboard.","We couldn't start card verification right now. Please try again."]}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/payment-methods/{method}":{"patch":{"operationId":"mePaymentMethod.update","summary":"Set this card as the member's primary or backup","tags":["Member App (self-service)"],"parameters":[{"name":"method","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["primary","backup"]}},"required":["role"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gateway":{"type":"string"},"scheme":{"type":["string","null"]},"last4":{"type":["string","null"]},"exp_month":{"type":["string","null"]},"exp_year":{"type":["string","null"]},"is_primary":{"type":"boolean"},"is_backup":{"type":"boolean"}},"required":["id","gateway","scheme","last4","exp_month","exp_year","is_primary","is_backup"]}}},"required":["data"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"mePaymentMethod.destroy","summary":"Remove a card \u2014 blocked if it's the member's last card while on a recurring plan (#20/#23)","tags":["Member App (self-service)"],"parameters":[{"name":"method","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gateway":{"type":"string"},"scheme":{"type":["string","null"]},"last4":{"type":["string","null"]},"exp_month":{"type":["string","null"]},"exp_year":{"type":["string","null"]},"is_primary":{"type":"boolean"},"is_backup":{"type":"boolean"}},"required":["id","gateway","scheme","last4","exp_month","exp_year","is_primary","is_backup"]}}},"required":["data"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"You must keep at least one card on file while on a recurring plan."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/wallet":{"get":{"operationId":"meWallet.show","summary":"Own balance + statement, same shape as the staff surface","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"balance_minor":{"type":"integer"},"currency":{"type":"string"},"ledger":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"amount_minor":{"type":"integer"},"balance_after_minor":{"type":"integer"},"source":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"description":"Localised label by transaction type; falls back to the (English) description.","anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"type":"string"}},"required":["id","type","amount_minor","balance_after_minor","source","description","description_ar","created_at"]}}},"required":["balance_minor","currency","ledger"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/wallet/topup":{"post":{"operationId":"meWallet.topup","summary":"Card-only top-up of the member's own wallet. Returns the gateway redirect; the wallet is\ncredited on settle by the PaymentSettled listener. Member app return URL","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer","minimum":1},"gateway":{"type":["string","null"],"description":"Optional chosen gateway (card processor or BNPL); must be one the brand has enabled."}},"required":["amount_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"redirect_url":{"type":["string","null"]}},"required":["payment_id","redirect_url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/wallet/auto-topup":{"get":{"operationId":"meWallet.autoTopup","summary":"GET /me/wallet/auto-topup \u2014 the member's auto-top-up rule (defaults if none set)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"threshold_minor":{"type":"integer"},"amount_minor":{"type":"integer"},"payment_method_id":{"type":["string","null"]}},"required":["enabled","threshold_minor","amount_minor","payment_method_id"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"meWallet.updateAutoTopup","summary":"PUT /me/wallet/auto-topup \u2014 set the member's auto-top-up rule (RFP 24 #34)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"threshold_minor":{"type":"integer","minimum":0},"amount_minor":{"type":"integer","minimum":0},"payment_method_id":{"type":["string","null"]}},"required":["enabled","threshold_minor","amount_minor"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/wallet/statement":{"get":{"operationId":"meWallet.statement","summary":"GET /me/wallet/statement \u2014 the member's wallet ledger as a downloadable PDF (RFP 24 #34)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/pdf":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string","const":"attachment; filename=\"memvera-wallet-statement.pdf\""}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/loyalty":{"get":{"operationId":"meLoyalty.show","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"points_balance":{"type":"integer"},"lifetime_points":{"type":"integer"},"tier":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"cashback_percent":{"type":"integer"},"points_multiplier":{"type":"number"}},"required":["id","name","name_ar","cashback_percent","points_multiplier"]},"next_tier":{"type":["object","null"],"properties":{"name":{"type":"string"},"name_ar":{"type":["string","null"]},"min_lifetime_points":{"type":"integer"},"points_to_go":{"type":["object","null"]}},"required":["name","name_ar","min_lifetime_points","points_to_go"]},"ledger":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"delta":{"type":"integer"},"reason":{"type":"string"},"balance_after":{"type":"integer"},"created_at":{"type":"string"}},"required":["id","delta","reason","balance_after","created_at"]}}},"required":["enabled","points_balance","lifetime_points","tier","next_tier","ledger"]},{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/points":{"get":{"operationId":"mePoints.index","summary":"GET /me/points \u2014 spendable balance + recent ledger (newest first)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"points_balance":{"type":"integer"},"ledger":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"delta":{"type":"integer"},"balance_after":{"type":"integer"},"reason":{"type":"string"},"order_id":{"type":["string","null"],"description":"Order linkage: the internal ledger keys movements by reference_type/reference_id;\nexpose the id only when it points at an order, else null (the app shows it as a tag)."},"created_at":{"type":"string"}},"required":["id","delta","balance_after","reason","order_id","created_at"]}}},"required":["points_balance","ledger"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/points/redeem":{"post":{"operationId":"mePoints.redeem","summary":"POST /me/points/redeem \u2014 convert points into wallet credit (atomic)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"points":{"type":"integer","minimum":1}},"required":["points"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"redeemed":{"type":"integer"},"wallet_credit_minor":{"type":"string"},"points_balance":{"type":"integer"}},"required":["redeemed","wallet_credit_minor","points_balance"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/challenges":{"get":{"operationId":"meChallenge.index","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"metric":{"type":"string"},"target":{"type":"integer"},"progress":{"type":["object","null"]},"period_type":{"type":"string"},"ends_on":{"type":["string","null"]},"reward_type":{"type":"string"},"reward_value":{"type":"integer"},"completed":{"anyOf":[{"type":"string"},{"type":"boolean"}]}},"required":["id","name","name_ar","description","description_ar","metric","target","progress","period_type","ends_on","reward_type","reward_value","completed"]}}},"required":["challenges"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/profile":{"get":{"operationId":"meCommunity.profile","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"streak_days":{"type":"integer"},"longest_streak":{"type":"integer"},"points":{"type":"integer"},"rank":{"type":"string"},"total_members":{"type":"integer","minimum":0},"badges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"earned":{"type":"boolean"},"earned_at":{"type":"string"}},"required":["id","icon_key","name","earned","earned_at"]}}},"required":["streak_days","longest_streak","points","rank","total_members","badges"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/feed":{"get":{"operationId":"meCommunity.feed","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string"},"author_member_id":{"type":["string","null"]},"author_name":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}]},"author_avatar":{"type":"string"},"author_is_coach":{"type":"boolean"},"is_mine":{"type":"string"},"group":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"group_id":{"type":["string","null"]},"kind":{"type":"string"},"body":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"created_at":{"type":"string"},"comments":{"type":"integer"},"reactions":{"type":"object","description":"counted from live rows (the denormalised counter can drift)","additionalProperties":{"type":"integer"}},"my_reaction":{},"pinned":{"type":"boolean"},"likes":{"type":"integer"},"liked":{"type":"boolean","description":"legacy: denormalised total (moves with reactions)"}},"required":["id","author_member_id","author_name","author_avatar","author_is_coach","is_mine","group","group_id","kind","body","created_at","comments","reactions","my_reaction","pinned","likes","liked"]}}},"required":["posts"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/achievements/shareable":{"get":{"operationId":"meCommunity.shareableAchievements","summary":"GET /me/community/achievements/shareable \u2014 my real achievements to share as a post","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"achievements":{"type":"array","items":{}}},"required":["achievements"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/posts":{"post":{"operationId":"meCommunity.createPost","summary":"POST /me/community/posts \u2014 publish a member post (optionally to a group / from an achievement)","tags":["Member App (self-service)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":["string","null"]},"group_id":{"type":["string","null"]},"achievement_id":{"type":["string","null"]}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"post":{"type":"object","properties":{"id":{"type":"string"},"author_member_id":{"type":["string","null"]},"author_name":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}]},"author_avatar":{"type":"string"},"author_is_coach":{"type":"boolean"},"is_mine":{"type":"string"},"group":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"group_id":{"type":["string","null"]},"kind":{"type":"string"},"body":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"created_at":{"type":"string"},"comments":{"type":"integer"},"reactions":{"type":"object","description":"counted from live rows (the denormalised counter can drift)","additionalProperties":{"type":"integer"}},"my_reaction":{},"pinned":{"type":"boolean"},"likes":{"type":"integer"},"liked":{"type":"boolean","description":"legacy: denormalised total (moves with reactions)"}},"required":["id","author_member_id","author_name","author_avatar","author_is_coach","is_mine","group","group_id","kind","body","created_at","comments","reactions","my_reaction","pinned","likes","liked"]}},"required":["post"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/posts/{post}":{"get":{"operationId":"meCommunity.post","summary":"GET /me/community/posts/{post} \u2014 a post + its comments","tags":["Member App (self-service)"],"parameters":[{"name":"post","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"post":{"type":"object","properties":{"id":{"type":"string"},"author_member_id":{"type":["string","null"]},"author_name":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}]},"author_avatar":{"type":"string"},"author_is_coach":{"type":"boolean"},"is_mine":{"type":"string"},"group":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"group_id":{"type":["string","null"]},"kind":{"type":"string"},"body":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"created_at":{"type":"string"},"comments":{"type":"integer"},"reactions":{"type":"object","description":"counted from live rows (the denormalised counter can drift)","additionalProperties":{"type":"integer"}},"my_reaction":{},"pinned":{"type":"boolean"},"likes":{"type":"integer"},"liked":{"type":"boolean","description":"legacy: denormalised total (moves with reactions)"}},"required":["id","author_member_id","author_name","author_avatar","author_is_coach","is_mine","group","group_id","kind","body","created_at","comments","reactions","my_reaction","pinned","likes","liked"]},"comments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"author_member_id":{"type":"string"},"author_name":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"],"const":"Member"},"en":{"type":"string","const":"Member"}},"required":["ar","en"]}]},"author_avatar":{"type":"string"},"body":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"created_at":{"type":"string"},"is_mine":{"type":"boolean"},"author_is_coach":{"type":"boolean"}},"required":["id","author_member_id","author_name","author_avatar","body","created_at","is_mine","author_is_coach"]}}},"required":["post","comments"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"meCommunity.deletePost","summary":"DELETE /me/community/posts/{post} \u2014 delete my own post","tags":["Member App (self-service)"],"parameters":[{"name":"post","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/posts/{post}/like":{"post":{"operationId":"meCommunity.like","tags":["Member App (self-service)"],"parameters":[{"name":"post","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"liked":{"type":"boolean"},"likes":{"type":"integer"}},"required":["liked","likes"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/posts/{post}/reaction":{"put":{"operationId":"meCommunity.reaction","summary":"PUT /me/community/posts/{post}/reaction \u2014 set (or clear, with null) my reaction","tags":["Member App (self-service)"],"parameters":[{"name":"post","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reaction":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"reactions":{"type":"object","additionalProperties":{"type":"integer"}},"my_reaction":{"type":["string","null"]}},"required":["ok","reactions","my_reaction"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/posts/{post}/comments":{"post":{"operationId":"meCommunity.addComment","summary":"POST /me/community/posts/{post}/comments \u2014 add a comment","tags":["Member App (self-service)"],"parameters":[{"name":"post","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":["string","null"]}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"comment":{"type":"object","properties":{"id":{"type":"string"},"author_member_id":{"type":"string"},"author_name":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"],"const":"Member"},"en":{"type":"string","const":"Member"}},"required":["ar","en"]}]},"author_avatar":{"type":"string"},"body":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"created_at":{"type":"string"},"is_mine":{"type":"boolean"},"author_is_coach":{"type":"boolean"}},"required":["id","author_member_id","author_name","author_avatar","body","created_at","is_mine","author_is_coach"]}},"required":["comment"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/posts/{post}/comments/{comment}":{"delete":{"operationId":"meCommunity.deleteComment","summary":"DELETE /me/community/posts/{post}/comments/{comment} \u2014 delete my comment","tags":["Member App (self-service)"],"parameters":[{"name":"post","in":"path","required":true,"schema":{"type":"string"}},{"name":"comment","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/groups":{"get":{"operationId":"meCommunity.groups","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"groups":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"description":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"members_count":{"type":"integer"},"joined":{"type":"boolean"}},"required":["id","icon_key","name","description","members_count","joined"]}}},"required":["groups"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/groups/{group}":{"get":{"operationId":"meCommunity.group","summary":"GET /me/community/groups/{group} \u2014 group detail + members + posts","tags":["Member App (self-service)"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"group":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"description":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"members_count":{"type":"string"},"joined":{"type":"boolean"}},"required":["id","icon_key","name","description","members_count","joined"]},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"},"is_coach":{"type":"boolean"}},"required":["id","name","avatar_url","is_me","is_coach"]}},"posts":{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string"},"author_member_id":{"type":["string","null"]},"author_name":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}]},"author_avatar":{"type":"string"},"author_is_coach":{"type":"boolean"},"is_mine":{"type":"string"},"group":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"group_id":{"type":["string","null"]},"kind":{"type":"string"},"body":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"created_at":{"type":"string"},"comments":{"type":"integer"},"reactions":{"type":"object","description":"counted from live rows (the denormalised counter can drift)","additionalProperties":{"type":"integer"}},"my_reaction":{},"pinned":{"type":"boolean"},"likes":{"type":"integer"},"liked":{"type":"boolean","description":"legacy: denormalised total (moves with reactions)"}},"required":["id","author_member_id","author_name","author_avatar","author_is_coach","is_mine","group","group_id","kind","body","created_at","comments","reactions","my_reaction","pinned","likes","liked"]}}},"required":["group","members","posts"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/groups/{group}/join":{"post":{"operationId":"meCommunity.joinGroup","tags":["Member App (self-service)"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"joined":{"type":"boolean"},"members_count":{"type":"integer"}},"required":["joined","members_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"meCommunity.leaveGroup","summary":"DELETE /me/community/groups/{group}/join \u2014 explicit leave","tags":["Member App (self-service)"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/members/{member}":{"get":{"operationId":"meCommunity.member","summary":"GET /me/community/members/{member} \u2014 a member's mini-profile + friendship bridge","tags":["Member App (self-service)"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"avatar_url":{"type":"string"},"is_me":{"type":"boolean"},"is_coach":{"anyOf":[{"type":"string"},{"type":"boolean"}]}},"required":["id","name","avatar_url","is_me","is_coach"]},"streak_days":{"anyOf":[{"type":"string"},{"type":"integer","enum":[0]}]},"points":{"type":"integer"},"badges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"earned":{"type":"boolean"}},"required":["id","icon_key","name","earned"]}},"is_friend":{"type":"boolean"},"request_sent":{"type":"boolean"}},"required":["member","streak_days","points","badges","is_friend","request_sent"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/reports":{"post":{"operationId":"meCommunity.report","summary":"POST /me/community/reports \u2014 flag a post or comment for staff review (UGC safety)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"target_type":{"type":"string"},"target_id":{"type":"string"},"reason":{"type":["string","null"],"maxLength":500}},"required":["target_type","target_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/nps/pending":{"get":{"operationId":"meNps.pending","summary":"GET /me/nps/pending \u2014 whether to show the pulse now + the brand's configured bilingual question","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"prompt":{"type":"string"},"question":{"type":"object","properties":{"en":{"type":"string"},"ar":{"type":"string"}},"required":["en","ar"]}},"required":["prompt","question"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/nps":{"post":{"operationId":"meNps.store","summary":"POST /me/nps {score, comment?} \u2014 record the answer","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"score":{"type":"integer","minimum":0,"maximum":10},"comment":{"type":["string","null"],"maxLength":500}},"required":["score"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"category":{"type":"string","enum":["promoter","passive","detractor"]}},"required":["ok","category"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pulses/pending":{"get":{"operationId":"mePulse.pending","summary":"GET /me/pulses/pending \u2014 the member's pulse queue (relational NPS first, then queued event pulses)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"pulses":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"pulse_prompt_id":{"type":"null"},"event":{"type":"string","const":"relational_nps"},"scale":{"type":"string","const":"nps"},"question":{"type":"object","properties":{"en":{"type":"string"},"ar":{"type":"string"}},"required":["en","ar"]},"subject":{"type":"null"}},"required":["pulse_prompt_id","event","scale","question","subject"]},{"type":"string"}]}}},"required":["pulses"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pulses":{"post":{"operationId":"mePulse.store","summary":"POST /me/pulses \u2014 record an answer.\nEvent pulse: {pulse_prompt_id, score?, comment?}. Relational NPS: {score} (0\u201310, comment optional)","tags":["Member App (self-service)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"pulse_prompt_id":{"type":["string","null"]},"score":{"type":["integer","null"],"minimum":0,"maximum":10},"comment":{"type":["string","null"],"maxLength":500}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"ok":{"type":"boolean"},"category":{"type":"string","enum":["promoter","passive","detractor"]}},"required":["ok","category"]},{"type":"object","properties":{"ok":{"type":"boolean"},"scale":{"type":"string"}},"required":["ok","scale"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/challenges":{"get":{"operationId":"meCommunity.challenges","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"description":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"unit":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"goal":{"type":"integer"},"progress":{"anyOf":[{"type":"string"},{"type":"integer","enum":[0]}]},"participants":{"type":"integer"},"joined":{"type":"boolean"},"ends_at":{"type":"string"},"reward_points":{"type":"integer"},"reward_minor":{"type":"integer"}},"required":["id","icon_key","title","description","unit","goal","progress","participants","joined","ends_at","reward_points","reward_minor"]}}},"required":["challenges"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/community/challenges/{challenge}/join":{"post":{"operationId":"meCommunity.joinChallenge","tags":["Member App (self-service)"],"parameters":[{"name":"challenge","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"joined":{"type":"boolean"},"participants":{"type":"integer"}},"required":["joined","participants"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/club-plans":{"get":{"operationId":"meClubPlan.index","summary":"GET /me/club-plans \u2014 the brand's public plan catalog (carousel, \"see all\", details all read this)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"cover_url":{"type":["string","null"]},"description":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"total_days":{"type":["integer","null"]},"min_daily_min":{"type":["string","null"]},"max_daily_min":{"type":["string","null"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"target_area":{"type":["string","null"]},"equipment":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"featured":{"type":"boolean"},"tags":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"benefits":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"schedule":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]}},"required":["id","title","cover_url","description","total_days","min_daily_min","max_daily_min","level","target_area","equipment","featured","tags","benefits","schedule"]}}},"required":["plans"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/club-plan":{"get":{"operationId":"meClubPlan.mine","summary":"GET /me/club-plan \u2014 the member's ONE joined club plan (+ progress), or null","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"club_plan":{"type":["object","null"],"properties":{"program_id":{"type":"string"},"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"total_days":{"type":["integer","null"]},"cover_url":{"type":["string","null"]}},"required":["id","title","total_days","cover_url"]},"completed_days":{"type":"array","items":{"type":"integer"}}},"required":["program_id","plan","completed_days"]}},"required":["club_plan"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/club-plans/{plan}/join":{"post":{"operationId":"meClubPlan.join","summary":"POST /me/club-plans/{plan}/join \u2014 join a public plan. Gated on an active membership (net-new); then\ndeep-clone the template into the member's club slot (the coach program is never touched)","tags":["Member App (self-service)"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"club_plan":{"type":"object","properties":{"program_id":{"type":"string"},"plan":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"total_days":{"type":["integer","null"]},"cover_url":{"type":["string","null"]}},"required":["id","title","total_days","cover_url"]},"completed_days":{"type":"array","items":{"type":"integer"}}},"required":["program_id","plan","completed_days"]}},"required":["club_plan"]}}}},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","enum":["\u062a\u062d\u062a\u0627\u062c \u0639\u0636\u0648\u064a\u0629 \u0646\u0634\u0637\u0629 \u0644\u0644\u0627\u0646\u0636\u0645\u0627\u0645 \u0625\u0644\u0649 \u062e\u0637\u0637 \u0627\u0644\u0646\u0627\u062f\u064a","You need an active membership to join club plans."]}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/club-plan/days/{dayNumber}":{"get":{"operationId":"meClubPlan.day","summary":"GET /me/club-plan/days/{dayNumber} \u2014 one 1-based day of the joined plan (same shape as a coach day)","tags":["Member App (self-service)"],"parameters":[{"name":"dayNumber","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"day":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"order":{"type":"integer"},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"],"description":"A timed exercise (plank/interval): duration_sec > 0 \u2192 hold for the time, ignore reps."},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]},"media":{"description":"Visual demo + how-to, sourced from the linked content-hub item (null-safe when unlinked).","anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"gif"},"url":{"type":"string"},"thumbnail_url":{"type":"string"}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"video"},"url":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"youtube"},"url":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"none"},"url":{"type":"null"},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"none"},"url":{"type":"null"},"thumbnail_url":{"type":"null"}},"required":["kind","url","thumbnail_url"]}]},"description":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"steps":{"type":"object","properties":{"en":{"type":"array","items":{"type":"string"}},"ar":{"type":"array","items":{"type":"string"}}},"required":["en","ar"]},"muscle":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"equipment":{"type":"array","items":{}},"done_sets":{"description":"Resume payload: what the member already logged for THIS exercise TODAY (KSA).","anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"last":{"type":["string","null"]}},"required":["id","name","sets","reps","duration_sec","rest_sec","target_weight_kg","note","workout_item_id","media","description","steps","muscle","equipment","done_sets","last"]}}},"required":["id","name","order","weekday","exercises"]}},"required":["day"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/program/active":{"get":{"operationId":"meProgram.active","summary":"GET /me/program/active \u2014 the member's active coach program (or {\"program\": null})","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"program":{"type":["object","null"],"properties":{"id":{"type":"string"},"coach":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name"]},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"weeks":{"type":"integer"},"current_week":{"type":"integer"},"assigned_at":{"type":"string"},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"order":{"type":"integer"},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"],"description":"A timed exercise (plank/interval): duration_sec > 0 \u2192 hold for the time, ignore reps."},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]},"media":{"description":"Visual demo + how-to, sourced from the linked content-hub item (null-safe when unlinked).","anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"gif"},"url":{"type":"string"},"thumbnail_url":{"type":"string"}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"video"},"url":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"youtube"},"url":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"none"},"url":{"type":"null"},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"none"},"url":{"type":"null"},"thumbnail_url":{"type":"null"}},"required":["kind","url","thumbnail_url"]}]},"description":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"steps":{"type":"object","properties":{"en":{"type":"array","items":{"type":"string"}},"ar":{"type":"array","items":{"type":"string"}}},"required":["en","ar"]},"muscle":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"equipment":{"type":"array","items":{}},"done_sets":{"description":"Resume payload: what the member already logged for THIS exercise TODAY (KSA).","anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"last":{"type":["string","null"]}},"required":["id","name","sets","reps","duration_sec","rest_sec","target_weight_kg","note","workout_item_id","media","description","steps","muscle","equipment","done_sets","last"]}}},"required":["id","name","order","weekday","exercises"]}},"completed_days":{"type":"array","description":"Additive (Club Plans, Phase 2): sequential day numbers already completed, for the weeks-map checkmarks.","items":{}}},"required":["id","coach","goal","weeks","current_week","assigned_at","days","completed_days"]}},"required":["program"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/program/today":{"get":{"operationId":"meProgram.today","summary":"GET /me/program/today \u2014 today's training day from the active program","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"date":{"type":"string"},"program_id":{"type":"string"},"day":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"order":{"type":"integer"},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"duration_sec":{"type":["integer","null"],"description":"A timed exercise (plank/interval): duration_sec > 0 \u2192 hold for the time, ignore reps."},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]},"media":{"description":"Visual demo + how-to, sourced from the linked content-hub item (null-safe when unlinked).","anyOf":[{"type":"object","properties":{"kind":{"type":"string","const":"gif"},"url":{"type":"string"},"thumbnail_url":{"type":"string"}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"video"},"url":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"youtube"},"url":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"none"},"url":{"type":"null"},"thumbnail_url":{"type":["string","null"]}},"required":["kind","url","thumbnail_url"]},{"type":"object","properties":{"kind":{"type":"string","const":"none"},"url":{"type":"null"},"thumbnail_url":{"type":"null"}},"required":["kind","url","thumbnail_url"]}]},"description":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"steps":{"type":"object","properties":{"en":{"type":"array","items":{"type":"string"}},"ar":{"type":"array","items":{"type":"string"}}},"required":["en","ar"]},"muscle":{"anyOf":[{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}]},"equipment":{"type":"array","items":{}},"done_sets":{"description":"Resume payload: what the member already logged for THIS exercise TODAY (KSA).","anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"last":{"type":["string","null"]}},"required":["id","name","sets","reps","duration_sec","rest_sec","target_weight_kg","note","workout_item_id","media","description","steps","muscle","equipment","done_sets","last"]}}},"required":["id","name","order","weekday","exercises"]},"completed":{"type":"string"},"rest_day":{"type":"string"}},"required":["date","program_id","day","completed","rest_day"]},{"type":"object","properties":{"date":{"type":"string"},"program_id":{"type":"null"},"day":{"type":"null"},"completed":{"type":"boolean"},"rest_day":{"type":"boolean"}},"required":["date","program_id","day","completed","rest_day"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/program/days/{day}/complete":{"post":{"operationId":"meProgram.complete","summary":"POST /me/program/days/{day}/complete {date?} \u2014 mark a program day done (adherence)","tags":["Member App (self-service)"],"parameters":[{"name":"day","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string","format":"date-time"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"completed_at":{"type":"string"}},"required":["ok","completed_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/summary":{"get":{"operationId":"meFitness.summary","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"workouts_this_week":{"type":"integer","minimum":0},"total_minutes":{"type":"integer"},"total_volume_kg":{"type":"integer"},"calories":{"type":"integer"},"active_streak":{"type":"integer","const":1}},"required":["workouts_this_week","total_minutes","total_volume_kg","calories","active_streak"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/goals":{"get":{"operationId":"meFitness.goals","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"goals":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"unit":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"current":{"anyOf":[{"type":"integer"},{"type":"number"}]},"target":{"anyOf":[{"type":"integer"},{"type":"number"}]},"deadline":{"type":"string"},"set_by":{"type":"string"}},"required":["id","icon_key","title","unit","current","target","deadline","set_by"]}}},"required":["goals"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meFitness.storeGoal","summary":"POST /me/fitness/goals \u2014 the member sets their own goal (D1)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"unit":{"type":["string","null"]},"current":{"type":"number"},"target":{"type":"number"},"deadline":{"type":["string","null"],"format":"date-time"},"icon_key":{"type":["string","null"],"maxLength":40}},"required":["title"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"goal":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"unit":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"current":{"anyOf":[{"type":"integer"},{"type":"number"}]},"target":{"anyOf":[{"type":"integer"},{"type":"number"}]},"deadline":{"type":"string"},"set_by":{"type":"string"}},"required":["id","icon_key","title","unit","current","target","deadline","set_by"]}},"required":["goal"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/goals/{goal}":{"patch":{"operationId":"meFitness.updateGoal","summary":"PATCH /me/fitness/goals/{goal} \u2014 update progress (D2)","tags":["Member App (self-service)"],"parameters":[{"name":"goal","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"current":{"type":"number"},"target":{"type":"number"},"deadline":{"type":["string","null"],"format":"date-time"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"goal":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"unit":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"current":{"anyOf":[{"type":"integer"},{"type":"number"}]},"target":{"anyOf":[{"type":"integer"},{"type":"number"}]},"deadline":{"type":"string"},"set_by":{"type":"string"}},"required":["id","icon_key","title","unit","current","target","deadline","set_by"]}},"required":["goal"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/personal-records":{"get":{"operationId":"meFitness.personalRecords","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"personal_records":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"exercise":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"value":{"anyOf":[{"type":"integer"},{"type":"number"}]},"unit":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"delta":{"anyOf":[{"type":"integer"},{"type":"number"}]},"achieved_at":{"type":"string"}},"required":["id","icon_key","exercise","value","unit","delta","achieved_at"]}}},"required":["personal_records"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/routines":{"get":{"operationId":"meFitness.routines","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"routines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"focus":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"level":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"exercise_count":{"type":"integer"},"duration_min":{"type":"integer"}},"required":["id","icon_key","name","focus","level","exercise_count","duration_min"]}}},"required":["routines"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/log":{"get":{"operationId":"meFitness.log","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"log":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"performed_at":{"type":"string"},"duration_min":{"type":"integer"},"volume_kg":{"type":"integer"},"calories":{"type":"integer"},"exercise_count":{"type":"integer"}},"required":["id","icon_key","title","performed_at","duration_min","volume_kg","calories","exercise_count"]}}},"required":["log"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meFitness.storeLog","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"routine_id":{"type":"string"}},"required":["routine_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"log":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"performed_at":{"type":"string"},"duration_min":{"type":"integer"},"volume_kg":{"type":"integer"},"calories":{"type":"integer"},"exercise_count":{"type":"integer"}},"required":["id","icon_key","title","performed_at","duration_min","volume_kg","calories","exercise_count"]}},"required":["log"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/library":{"get":{"operationId":"meFitness.library","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"muscle_group":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"equipment":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"difficulty":{"type":"string"},"description":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]}},"required":["id","icon_key","name","muscle_group","equipment","difficulty","description"]}}},"required":["exercises"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/routines/{routine}/share-review":{"post":{"operationId":"meFitness.shareRoutine","summary":"POST /me/fitness/routines/{routine}/share-review \u2014 share a routine with the coach.\nThe exercises snapshot travels in the payload (routines store aggregates only);\nthe trainer defaults to the member's latest PT coach when not specified","tags":["Member App (self-service)"],"parameters":[{"name":"routine","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"trainer_id":{"type":["string","null"]},"exercises":{"type":["array","null"],"items":{"type":"object","properties":{"name":{"type":"string","maxLength":160},"sets_reps":{"type":["string","null"],"maxLength":20}},"required":["name"]},"maxItems":30}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"review_id":{"type":"string"},"status":{"type":"string","const":"pending"}},"required":["review_id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/sets":{"post":{"operationId":"meFitness.logSet","summary":"POST /me/fitness/sets \u2014 log a single set (reps/weight/RPE per exercise). This is the\ngranular RFP FITC workout-logging; each set is its own row, optionally grouped by a\nworkout-log session and linked to a hub exercise","tags":["Member App (self-service)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"exercise":{"type":"string","maxLength":160},"exercise_ar":{"type":["string","null"],"maxLength":160},"workout_item_id":{"type":["string","null"]},"fitness_workout_log_id":{"type":["string","null"]},"trainer_program_exercise_id":{"type":["string","null"]},"set_no":{"type":["integer","null"],"minimum":1,"maximum":100},"reps":{"type":["integer","null"],"minimum":0,"maximum":1000},"duration_sec":{"type":["integer","null"],"minimum":1,"maximum":3600},"weight_kg":{"type":["number","null"],"minimum":0,"maximum":1000},"rpe":{"type":["integer","null"],"minimum":1,"maximum":10}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"set":{"type":"object","properties":{"id":{"type":"string"},"exercise":{"type":"string"},"set_no":{"type":"integer"},"reps":{"type":["integer","null"]},"weight_kg":{"type":["number","null"]},"rpe":{"type":["integer","null"]},"target":{"type":["object","null"],"properties":{"sets":{"type":"integer"},"reps":{"type":"integer"},"weight_kg":{"type":["number","null"]}},"required":["sets","reps","weight_kg"]}},"required":["id","exercise","set_no","reps","weight_kg","rpe","target"]}},"required":["set"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"get":{"operationId":"meFitness.sets","summary":"GET /me/fitness/sets?exercise= \u2014 set history for an exercise + the heaviest set (PR)","tags":["Member App (self-service)"],"parameters":[{"name":"exercise","in":"query","schema":{"type":["string","null"],"maxLength":160}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"exercise":{"type":"string"},"set_no":{"type":"integer"},"reps":{"type":["integer","null"]},"weight_kg":{"type":["number","null"]},"rpe":{"type":["integer","null"]},"performed_at":{"type":"string"}},"required":["id","exercise","set_no","reps","weight_kg","rpe","performed_at"]}},"pr":{"type":["object","null"],"properties":{"weight_kg":{"type":"number"},"reps":{"type":["integer","null"]}},"required":["weight_kg","reps"]}},"required":["sets","pr"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/fitness/body-weight":{"post":{"operationId":"meFitness.logBodyWeight","summary":"POST /me/fitness/body-weight \u2014 the member records a weigh-in (feeds the coach body-weight chart\nand the member's progress). One point per day; a same-day re-weigh overwrites. `recorded_on`\ndefaults to today in KSA time (the app timezone is UTC)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"weight_kg":{"type":"number","minimum":20,"maximum":400},"recorded_on":{"type":["string","null"],"format":"date-time"}},"required":["weight_kg"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"body_weight":{"type":"object","properties":{"date":{"type":"string"},"weight_kg":{"type":"number"},"source":{"type":"string"}},"required":["date","weight_kg","source"]}},"required":["body_weight"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/chat/token":{"post":{"operationId":"meChatToken.store","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"url":{"type":"string"},"expires_in":{"type":"integer","const":3600}},"required":["token","url","expires_in"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts":{"get":{"operationId":"meWorkout.index","summary":"GET /me/workouts \u2014 the BRAND's hub: featured items + the brand's GROUPS as sections\n(\"Chest Day\", \"Ramadan Program\"\u2026). A group is the only door into the hub, groups\nfreely mix providers, and the provider stays invisible to members","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"featured":{"type":"string"},"categories":{"type":"string"},"sections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"display_name":{"type":"string"},"display_name_ar":{"type":["string","null"]},"cover_url":{"type":["string","null"]},"items":{"type":"string"}},"required":["id","display_name","display_name_ar","cover_url","items"]}}},"required":["featured","categories","sections"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/catalog":{"get":{"operationId":"meWorkout.catalog","summary":"GET /me/workouts/catalog?provider=&category=&level=&q=&page= \u2014 paginated browse","tags":["Member App (self-service)"],"parameters":[{"name":"provider","in":"query","schema":{"type":["string","null"]}},{"name":"category","in":"query","schema":{"type":["string","null"],"maxLength":60}},{"name":"level","in":"query","schema":{"type":["string","null"],"maxLength":20}},{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":120}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"],"description":"Licensed assets go through the media proxy (the provider key never leaves the server)."},"duration_seconds":{"type":["integer","null"]},"level":{"type":["string","null"]},"category":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"instructor":{"type":["string","null"]},"is_premium":{"type":"boolean"}},"required":["id","provider","type","title","title_ar","thumbnail_url","duration_seconds","level","category","category_ar","instructor","is_premium"]}},"page":{"type":"integer"},"last_page":{"type":"integer"},"total":{"type":"integer"}},"required":["items","page","last_page","total"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/favorites/list":{"get":{"operationId":"meWorkout.favorites","summary":"GET /me/workouts/favorites/list \u2014 the member's saved items","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"thumbnail_url":{"type":["string","null"],"description":"Licensed assets go through the media proxy (the provider key never leaves the server)."},"duration_seconds":{"type":["integer","null"]},"level":{"type":["string","null"]},"category":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"instructor":{"type":["string","null"]},"is_premium":{"type":"boolean"}},"required":["id","provider","type","title","title_ar","thumbnail_url","duration_seconds","level","category","category_ar","instructor","is_premium"]}}},"required":["items"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/groups/{group}":{"get":{"operationId":"meWorkout.group","summary":"GET /me/workouts/groups/{group} \u2014 one group in full","tags":["Member App (self-service)"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"group":{"type":"object","properties":{"id":{"type":"string"},"display_name":{"type":"string"},"display_name_ar":{"type":"string"},"description":{"type":"string"},"description_ar":{"type":"string"},"category":{"type":"string"},"category_ar":{"type":"string"},"cover_url":{"type":"string"},"items":{"type":"string"}},"required":["id","display_name","display_name_ar","description","description_ar","category","category_ar","cover_url","items"]}},"required":["group"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/wods":{"get":{"operationId":"meWod.index","summary":"GET /me/workouts/wods?from=&to= \u2014 a date-ranged list (calendar/history)","tags":["Member App (self-service)"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"track","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"wods":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"track":{"type":"string"},"date":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"scoring_type":{"type":"string"},"allow_scaled":{"type":"boolean"},"movements":{"type":"array","items":{}},"sections":{"type":"array","description":"back-compat (flat)","items":{"type":"object","properties":{"key":{"anyOf":[{"type":"string"},{"type":"string","enum":["wod"]}]},"title":{"type":["string","null"]},"title_ar":{"type":["string","null"]},"movements":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"scored":{"type":"boolean"},"scoring_type":{"anyOf":[{"type":"string"},{"type":"string","enum":["none"]}]},"leaderboard":{"type":"boolean"},"my_score":{"type":["object","null"],"properties":{"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":"number"},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"},"unit":{"type":"string"}},"required":["scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at","unit"]}},"required":["key","title","title_ar","movements","scored","scoring_type","leaderboard","my_score"]}},"leaderboard_section_key":{"anyOf":[{"type":"string"},{"type":"string","enum":["wod"]}]},"weight_unit":{"type":"string"},"score_count":{"type":"integer","minimum":0},"my_score":{"type":["object","null"],"properties":{"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":"number"},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"},"unit":{"type":"string"}},"required":["scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at","unit"]}},"required":["id","track","date","title","title_ar","description","description_ar","scoring_type","allow_scaled","movements","sections","leaderboard_section_key","weight_unit","score_count","my_score"]}}},"required":["wods"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/wods/today":{"get":{"operationId":"meWod.today","summary":"GET /me/workouts/wods/today \u2014 the day's single published training workout (with its sections)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string"},"wod":{"type":["object","null"],"properties":{"id":{"type":"string"},"track":{"type":"string"},"date":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"scoring_type":{"type":"string"},"allow_scaled":{"type":"boolean"},"movements":{"type":"array","items":{}},"sections":{"type":"array","description":"back-compat (flat)","items":{"type":"object","properties":{"key":{"anyOf":[{"type":"string"},{"type":"string","enum":["wod"]}]},"title":{"type":["string","null"]},"title_ar":{"type":["string","null"]},"movements":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"scored":{"type":"boolean"},"scoring_type":{"anyOf":[{"type":"string"},{"type":"string","enum":["none"]}]},"leaderboard":{"type":"boolean"},"my_score":{"type":["object","null"],"properties":{"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":"number"},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"},"unit":{"type":"string"}},"required":["scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at","unit"]}},"required":["key","title","title_ar","movements","scored","scoring_type","leaderboard","my_score"]}},"leaderboard_section_key":{"anyOf":[{"type":"string"},{"type":"string","enum":["wod"]}]},"weight_unit":{"type":"string"},"score_count":{"type":"integer","minimum":0},"my_score":{"type":["object","null"],"properties":{"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":"number"},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"},"unit":{"type":"string"}},"required":["scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at","unit"]}},"required":["id","track","date","title","title_ar","description","description_ar","scoring_type","allow_scaled","movements","sections","leaderboard_section_key","weight_unit","score_count","my_score"]}},"required":["date","wod"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/wods/{wod}":{"get":{"operationId":"meWod.show","summary":"GET /me/workouts/wods/{wod} \u2014 WOD detail + my score","tags":["Member App (self-service)"],"parameters":[{"name":"wod","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"wod":{"type":"object","properties":{"id":{"type":"string"},"track":{"type":"string"},"date":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"scoring_type":{"type":"string"},"allow_scaled":{"type":"boolean"},"movements":{"type":"array","items":{}},"sections":{"type":"array","description":"back-compat (flat)","items":{"type":"object","properties":{"key":{"anyOf":[{"type":"string"},{"type":"string","enum":["wod"]}]},"title":{"type":["string","null"]},"title_ar":{"type":["string","null"]},"movements":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"scored":{"type":"boolean"},"scoring_type":{"anyOf":[{"type":"string"},{"type":"string","enum":["none"]}]},"leaderboard":{"type":"boolean"},"my_score":{"type":["object","null"],"properties":{"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":"number"},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"},"unit":{"type":"string"}},"required":["scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at","unit"]}},"required":["key","title","title_ar","movements","scored","scoring_type","leaderboard","my_score"]}},"leaderboard_section_key":{"anyOf":[{"type":"string"},{"type":"string","enum":["wod"]}]},"weight_unit":{"type":"string"},"score_count":{"type":"integer","minimum":0},"my_score":{"type":["object","null"],"properties":{"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":"number"},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"},"unit":{"type":"string"}},"required":["scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at","unit"]}},"required":["id","track","date","title","title_ar","description","description_ar","scoring_type","allow_scaled","movements","sections","leaderboard_section_key","weight_unit","score_count","my_score"]}},"required":["wod"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/wods/{wod}/leaderboard":{"get":{"operationId":"meWod.leaderboard","summary":"GET /me/workouts/wods/{wod}/leaderboard?scale=&gender= \u2014 ranked board","tags":["Member App (self-service)"],"parameters":[{"name":"wod","in":"path","required":true,"schema":{"type":"string"}},{"name":"scale","in":"query","schema":{"type":["string","null"],"enum":["rx","scaled","all"]}},{"name":"gender","in":"query","schema":{"type":["string","null"],"enum":["male","female","all"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"wod":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"scoring_type":{"type":"string"}},"required":["id","title","scoring_type"]},"scale":{"anyOf":[{"type":"string"},{"type":"string","enum":["all"]}]},"gender":{"anyOf":[{"type":"string"},{"type":"string","enum":["all"]}]},"weight_unit":{"type":"string"},"entries":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"string"},"member":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"initial":{"type":"string"},"is_me":{"type":"string"}},"required":["id","name","initial","is_me"]},"result":{"type":"string"},"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":["number","null"]},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"}},"required":["rank","member","result","scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at"]}},"my_rank":{"type":["string","null"]},"total":{"type":"integer"}},"required":["wod","scale","gender","weight_unit","entries","my_rank","total"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/wods/{wod}/score":{"post":{"operationId":"meWod.score","summary":"POST /me/workouts/wods/{wod}/score \u2014 \"Register Your Score\" for one section (default the WOD).\nThe WOD (leaderboard) section awards participation points; a Strength/Skill section stays out\nof the board and instead updates the member's personal records","tags":["Member App (self-service)"],"parameters":[{"name":"wod","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"section_key":{"type":"string","maxLength":40},"weight_unit":{"type":"string","enum":["kg","lb"]},"scale":{"type":["string","null"],"enum":["rx","scaled"]},"value_seconds":{"type":["integer","null"],"minimum":1,"maximum":86400},"value_numeric":{"type":["number","null"],"minimum":0,"maximum":100000},"rounds":{"type":["integer","null"],"minimum":0,"maximum":10000},"reps":{"type":["integer","null"],"minimum":0,"maximum":100000},"notes":{"type":["string","null"],"maxLength":1000}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"score":{"type":"object","properties":{"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":"number"},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"},"unit":{"type":"string"}},"required":["scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at","unit"]},"weight_unit":{"type":"string"}},"required":["score","weight_unit"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"meWod.deleteScore","summary":"DELETE /me/workouts/wods/{wod}/score?section_key= \u2014 remove my result for a section","tags":["Member App (self-service)"],"parameters":[{"name":"wod","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/benchmarks":{"get":{"operationId":"meBenchmark.index","summary":"GET /me/workouts/benchmarks \u2014 active benchmarks with my PR each","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"benchmarks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"category":{"type":["string","null"]},"scoring_type":{"type":"string"},"my_pr":{"type":["object","null"],"properties":{"result":{"type":"string"},"performed_on":{"type":"string"},"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":["number","null"]},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"}},"required":["result","performed_on","scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at"]}},"required":["id","name","name_ar","category","scoring_type","my_pr"]}}},"required":["benchmarks"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/benchmarks/{benchmark}":{"get":{"operationId":"meBenchmark.show","summary":"GET /me/workouts/benchmarks/{benchmark} \u2014 detail + my attempt history + PR","tags":["Member App (self-service)"],"parameters":[{"name":"benchmark","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"benchmark":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"description":{"type":"string"},"description_ar":{"type":"string"},"category":{"type":"string"},"scoring_type":{"type":"string"},"my_pr":{"type":["object","null"],"properties":{"result":{"type":"string"},"performed_on":{"type":"string"},"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":["number","null"]},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"}},"required":["result","performed_on","scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at"]},"my_history":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"performed_on":{"type":"string"},"result":{"type":"string"},"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":["number","null"]},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"}},"required":["id","performed_on","result","scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at"]}}},"required":["id","name","name_ar","description","description_ar","category","scoring_type","my_pr","my_history"]}},"required":["benchmark"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/benchmarks/{benchmark}/leaderboard":{"get":{"operationId":"meBenchmark.leaderboard","summary":"GET /me/workouts/benchmarks/{benchmark}/leaderboard?scale=&gender= \u2014 best per member","tags":["Member App (self-service)"],"parameters":[{"name":"benchmark","in":"path","required":true,"schema":{"type":"string"}},{"name":"scale","in":"query","schema":{"type":["string","null"],"enum":["rx","scaled","all"]}},{"name":"gender","in":"query","schema":{"type":["string","null"],"enum":["male","female","all"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"benchmark":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"scoring_type":{"type":"string"}},"required":["id","name","scoring_type"]},"scale":{"anyOf":[{"type":"string"},{"type":"string","enum":["all"]}]},"gender":{"anyOf":[{"type":"string"},{"type":"string","enum":["all"]}]},"entries":{"type":"array","items":{"type":"object","properties":{"rank":{"type":"string"},"member":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"initial":{"type":"string"},"is_me":{"type":"string"}},"required":["id","name","initial","is_me"]},"result":{"type":"string"},"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":["number","null"]},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"}},"required":["rank","member","result","scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at"]}},"my_rank":{"type":["string","null"]},"total":{"type":"integer"}},"required":["benchmark","scale","gender","entries","my_rank","total"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/benchmarks/{benchmark}/results":{"post":{"operationId":"meBenchmark.log","summary":"POST /me/workouts/benchmarks/{benchmark}/results \u2014 log an attempt","tags":["Member App (self-service)"],"parameters":[{"name":"benchmark","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"scale":{"type":["string","null"],"enum":["rx","scaled"]},"value_seconds":{"type":["integer","null"],"minimum":1,"maximum":86400},"value_numeric":{"type":["number","null"],"minimum":0,"maximum":100000},"rounds":{"type":["integer","null"],"minimum":0,"maximum":10000},"reps":{"type":["integer","null"],"minimum":0,"maximum":100000},"notes":{"type":["string","null"],"maxLength":1000},"performed_on":{"type":["string","null"],"format":"date-time"}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"result":{"type":"object","properties":{"id":{"type":"string"},"result":{"type":"string"}},"required":["id","result"]},"is_pr":{"type":"string"},"my_pr":{"type":["object","null"],"properties":{"result":{"type":"string"},"performed_on":{"type":"string"},"scale":{"type":"string"},"value_seconds":{"type":"string"},"value_numeric":{"type":["number","null"]},"entered_unit":{"type":["string","null"]},"rounds":{"type":"string"},"reps":{"type":"string"},"notes":{"type":"string"},"performed_at":{"type":"string"}},"required":["result","performed_on","scale","value_seconds","value_numeric","entered_unit","rounds","reps","notes","performed_at"]}},"required":["result","is_pr","my_pr"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/{item}":{"get":{"operationId":"meWorkout.show","summary":"GET /me/workouts/{item} \u2014 detail + the server-resolved playback envelope","tags":["Member App (self-service)"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"item":{"type":"string"}},"required":["item"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/{item}/complete":{"post":{"operationId":"meWorkout.complete","summary":"POST /me/workouts/{item}/complete \u2014 a finished content workout writes the member's\nfitness log (same timeline as gym workouts), which feeds challenges/streaks","tags":["Member App (self-service)"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"completed":{"type":"boolean"}},"required":["completed"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/workouts/{item}/favorite":{"post":{"operationId":"meWorkout.favorite","summary":"POST /me/workouts/{item}/favorite + DELETE \u2014 toggle a saved item","tags":["Member App (self-service)"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"favorite":{"type":"boolean"}},"required":["favorite"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"meWorkout.unfavorite","tags":["Member App (self-service)"],"parameters":[{"name":"item","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"favorite":{"type":"boolean"}},"required":["favorite"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/support/channels":{"get":{"operationId":"meSupport.channels","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"label":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"value":{"type":"string"},"availability":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]}},"required":["type","label","value","availability"]}}},"required":["channels"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/support/faqs":{"get":{"operationId":"meSupport.faqs","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"faqs":{"type":"array","items":{"type":"object","properties":{"question":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"answer":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["question","answer"]}}},"required":["faqs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/support/tickets":{"get":{"operationId":"meSupport.tickets","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","subject","category","status","created_at","updated_at","messages"]}}},"required":["tickets"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meSupport.store","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"subject":{"type":"string","maxLength":200},"category":{"type":"string","enum":["billing","membership","technical","other"]},"body":{"type":"string","maxLength":5000}},"required":["subject","category","body"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","subject","category","status","created_at","updated_at","messages"]}},"required":["ticket"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/support/tickets/{ticket}":{"get":{"operationId":"meSupport.show","tags":["Member App (self-service)"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","subject","category","status","created_at","updated_at","messages"]}},"required":["ticket"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/support/tickets/{ticket}/reply":{"post":{"operationId":"meSupport.reply","tags":["Member App (self-service)"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","maxLength":5000}},"required":["body"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","subject","category","status","created_at","updated_at","messages"]}},"required":["ticket"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/complaints":{"get":{"operationId":"meComplaint.index","summary":"GET /me/complaints \u2014 the member's own complaints, newest first","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"complaints":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"category":{"type":"string"},"body":{"type":["string","null"]},"club_id":{"type":["string","null"]},"club_name":{},"has_image":{"type":"boolean"},"status":{"type":"string"},"created_at":{"type":"string"},"resolved_at":{"type":"string"}},"required":["id","category","body","club_id","club_name","has_image","status","created_at","resolved_at"]}}},"required":["complaints"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meComplaint.store","summary":"POST /me/complaints \u2014 file a complaint (description + category + branch + optional photo)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string"},"body":{"type":"string","maxLength":2000},"club_id":{"type":["string","null"],"description":"Branch must be a real club in the member's brand (BelongsToBrand scopes the lookup)."},"image":{"type":["string","null"],"description":"Optional photo as a data-URL, capped ~4MB of base64.","maxLength":5600000}},"required":["category","body"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/classes":{"get":{"operationId":"meBooking.classes","summary":"GET /me/classes \u2014 upcoming bookable sessions (next ~14 days), soonest first. Branch-scoped: the\nmember browses one branch at a time. `club_id` picks the branch; when omitted it defaults to the\nmember's home branch (their active contract's club). The response also carries the full branch list\n(every branch with upcoming sessions) + `home_club_id` so the app can render the branch selector","tags":["Member App (self-service)"],"parameters":[{"name":"club_id","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"string"},"clubs":{"type":"array","description":"Branch selector data: all branches with upcoming sessions + the member's home branch.","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]}},"home_club_id":{"type":["string","null"]},"selected_club_id":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["sessions","clubs","home_club_id","selected_club_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/classes/{session}/workout":{"get":{"operationId":"meClassWorkout.show","summary":"GET /me/classes/{session}/workout \u2014 the workout for a session, scaled to the authenticated member","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"workout":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"notes":{"type":["string","null"]},"notes_ar":{"type":["string","null"]},"member_level":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]},"sections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"scheme":{"type":"string"},"rounds":{"type":["integer","null"]},"window_sec":{"type":["integer","null"]},"time_cap_sec":{"type":["integer","null"]},"note":{"type":["string","null"]},"note_ar":{"type":["string","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"fitness_exercise_id":{"type":["string","null"]},"reps":{"type":["integer","null"]},"reps_text":{"type":["string","null"]},"duration_sec":{"type":["integer","null"]},"load_type":{"type":"string"},"load_value":{"type":["number","null"]},"load_value_max":{"type":["number","null"]},"load_unit":{"type":["string","null"]},"sets":{"type":["array","null"],"items":{}},"note":{"type":["string","null"]},"note_ar":{"type":["string","null"]},"levels":{"type":"array","description":"Every level's prescription (staff builder / \"other levels\" chip).","items":{"type":"object","properties":{"level_id":{"type":"string"},"load_value":{"type":["number","null"]},"load_unit":{"type":["string","null"]},"reps":{"type":["integer","null"]},"note":{"type":["string","null"]}},"required":["level_id","load_value","load_unit","reps","note"]}},"member_load":{"description":"The one prescription THIS member should read.","anyOf":[{"type":"object","properties":{"display":{"type":"null"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"none"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string","const":"BW"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"bodyweight"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"rpe"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"anyOf":[{"type":"string"},{"type":"string","enum":["% 1RM"]}]},"weight_kg":{"type":"null"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"percent_only"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"number"},"reps":{"type":["integer","null"]},"source":{"type":"string","const":"one_rm"}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"string"},"weight_kg":{"type":"number"},"reps":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"source":{"type":"string","enum":["level","base"]}},"required":["display","weight_kg","reps","source"]},{"type":"object","properties":{"display":{"type":"null"},"weight_kg":{"type":"null"},"reps":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"source":{"type":"string","const":"none"}},"required":["display","weight_kg","reps","source"]},{"type":"null"}]}},"required":["id","name","name_ar","fitness_exercise_id","reps","reps_text","duration_sec","load_type","load_value","load_value_max","load_unit","sets","note","note_ar","levels","member_load"]}}},"required":["id","title","title_ar","scheme","rounds","window_sec","time_cap_sec","note","note_ar","exercises"]}}},"required":["id","title","title_ar","notes","notes_ar","member_level","sections"]}},"required":["workout"]},{"type":"object","properties":{"workout":{"type":"null"}},"required":["workout"]}]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/bookings":{"get":{"operationId":"meBooking.index","summary":"GET /me/bookings \u2014 the member's own bookings, newest session first","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"session_id":{"type":"string"},"concept_name":{"type":"string"},"concept_name_ar":{"type":"string"},"category":{"type":"string"},"category_ar":{"type":"string"},"image_url":{"type":"string"},"club_name":{"type":["string","null"]},"starts_at":{"type":"string"},"status":{"type":"string"},"waitlist_position":{"type":["integer","null"]},"rated":{"type":"boolean","description":"RFP 14 #22 \u2014 only confirmed attendees of a conducted session can rate, once."},"ratable":{"type":"string"},"checked_in":{"type":"boolean","description":"Two-key attendance: the member key state + whether the app-confirm path is open now."},"can_confirm":{"type":"boolean"}},"required":["id","session_id","concept_name","concept_name_ar","category","category_ar","image_url","club_name","starts_at","status","waitlist_position","rated","ratable","checked_in","can_confirm"]}}},"required":["bookings"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meBooking.store","summary":"POST /me/bookings { session_id, access_code? } \u2014 book the AUTHENTICATED member into a session","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string"},"access_code":{"type":["string","null"]}},"required":["session_id"]}}}},"responses":{"201":{"description":"Tell the member whether they got a seat or a waitlist place (+ position) (RFP 24 #28).","content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"},"status":{"type":"string"},"waitlist_position":{"type":["integer","null"]}},"required":["booking_id","status","waitlist_position"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/bookings/{booking}/reschedule":{"post":{"operationId":"meBooking.reschedule","summary":"POST /me/bookings/{booking}/reschedule { session_id } \u2014 move the member's OWN booking to\nanother session (RFP 24 #28). Books the new session first (so a failure leaves the original\nintact), then cancels the old one \u2014 which promotes the head of that session's waitlist","tags":["Member App (self-service)"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_id":{"type":"string"}},"required":["session_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"},"status":{"type":"string"},"waitlist_position":{"type":["integer","null"]}},"required":["booking_id","status","waitlist_position"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/bookings/{booking}/rate":{"post":{"operationId":"meBooking.rate","summary":"POST /me/bookings/{booking}/rate \u2014 rate a conducted session (RFP 14 #22). Only the member's own\nCONDUCTED booking can be rated, once: class + instructor (1\u20135 stars) + an optional comment","tags":["Member App (self-service)"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"class_stars":{"type":"integer","minimum":1,"maximum":5},"trainer_stars":{"type":["integer","null"],"minimum":1,"maximum":5},"comment":{"type":["string","null"],"maxLength":1000}},"required":["class_stars"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rated":{"type":"boolean"}},"required":["rated"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/bookings/{booking}/claim":{"post":{"operationId":"meBooking.claim","summary":"POST /me/bookings/{booking}/claim \u2014 claim a live waitlist offer (RFP refinement)","tags":["Member App (self-service)"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/bookings/{booking}/confirm":{"post":{"operationId":"meBooking.confirm","summary":"POST /me/bookings/{booking}/confirm \u2014 the member turns their attendance key from the\napp (\"I'm here\"). Only when the brand policy allows the app source for this session\ntype, and only inside the confirm window (starts\u2212N min \u2192 end). Gate check-in remains\nthe strongest evidence and is never replaced by this","tags":["Member App (self-service)"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"checked_in":{"type":"boolean"}},"required":["id","checked_in"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"Attendance cannot be confirmed from the app right now."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/bookings/{booking}":{"delete":{"operationId":"meBooking.destroy","summary":"DELETE /me/bookings/{booking} \u2014 cancel the member's OWN booking (ownership-guarded)","tags":["Member App (self-service)"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"late_cancel":{"type":"boolean"}},"required":["status","late_cancel"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/strikes":{"get":{"operationId":"strike.mine","summary":"GET /me/strikes \u2014 the authenticated member's own discipline record","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"active_strikes":{"type":"integer","minimum":0},"threshold":{"type":"integer"},"window_days":{"type":"integer"},"active_ban":{"type":["object","null"],"properties":{"ends_at":{"type":"string"},"reason":{"type":"string"}},"required":["ends_at","reason"]},"strikes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string"},"created_at":{"type":"string","description":"no_show | late_cancel | non_confirmation"},"exempt":{"type":"boolean"},"exempt_reason":{"type":["string","null"]},"consumed":{"type":"boolean","description":"promoted_waitlist | illness | admin_waiver"},"waived":{"type":"boolean"},"waive_reason":{"type":["string","null"]},"waived_by":{},"counts":{"type":"boolean"}},"required":["id","reason","created_at","exempt","exempt_reason","consumed","waived","waive_reason","waived_by","counts"]}},"bans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"strike_count":{"type":"integer"},"active":{"type":"string","description":"Active = still within its window AND not lifted early by a manager."},"lifted":{"type":"boolean"},"lifted_at":{"type":"string"},"lift_reason":{"type":["string","null"]},"lifted_by":{}},"required":["id","reason","starts_at","ends_at","strike_count","active","lifted","lifted_at","lift_reason","lifted_by"]}}},"required":["active_strikes","threshold","window_days","active_ban","strikes","bans"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pt/options":{"get":{"operationId":"mePt.options","summary":"GET /me/pt/options \u2014 the member's usable PT packages + bookable branches","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"packages":{"type":"array","items":{"type":"object","properties":{"grant_id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"string","enum":["PT package"]}]},"credits_remaining":{"type":"integer"},"credits_total":{"type":"integer"},"expires_at":{"type":"string"},"club_ids":{"type":"string"}},"required":["grant_id","name","credits_remaining","credits_total","expires_at","club_ids"]}},"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"city":{"type":["string","null"]}},"required":["id","name","name_ar","city"]}}},"required":["packages","clubs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pt/trainers":{"get":{"operationId":"mePt.trainers","summary":"GET /me/pt/trainers?club_id= \u2014 PT-eligible trainers at a branch","tags":["Member App (self-service)"],"parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"trainers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"photo_url":{"type":["string","null"]}},"required":["id","name","name_ar","photo_url"]}}},"required":["trainers"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pt/slots":{"get":{"operationId":"mePt.slots","summary":"GET /me/pt/slots?trainer_id=&date= \u2014 open start-times for a trainer on a date","tags":["Member App (self-service)"],"parameters":[{"name":"trainer_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"slots":{"type":"array","items":{"type":"object","properties":{"starts_at":{"type":"string"},"label":{"type":"string"}},"required":["starts_at","label"]}}},"required":["slots"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pt/book":{"post":{"operationId":"mePt.book","summary":"POST /me/pt/book \u2014 reserve a credit and book the chosen trainer/slot","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"trainer_id":{"type":"string"},"starts_at":{"type":"string","format":"date-time"}},"required":["club_id","trainer_id","starts_at"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"},"session_id":{"type":"string"},"status":{"type":"string"}},"required":["booking_id","session_id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pt/bookings":{"get":{"operationId":"mePt.bookings","summary":"GET /me/pt/bookings \u2014 the member's PT bookings (upcoming + past)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"starts_at":{"type":"string"},"club_name":{"type":["string","null"]},"trainer_name":{"type":["string","null"]},"cancellable":{"type":"string"},"rated":{"type":"boolean"},"ratable":{"type":"string"}},"required":["id","status","starts_at","club_name","trainer_name","cancellable","rated","ratable"]}}},"required":["bookings"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pt/bookings/{booking}/reschedule":{"post":{"operationId":"mePt.reschedule","summary":"POST /me/pt/bookings/{booking}/reschedule \u2014 move the booking to a new trainer/slot (no penalty)","tags":["Member App (self-service)"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"trainer_id":{"type":"string"},"starts_at":{"type":"string","format":"date-time"}},"required":["club_id","trainer_id","starts_at"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"},"session_id":{"type":"string"},"status":{"type":"string"}},"required":["booking_id","session_id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/pt/bookings/{booking}":{"delete":{"operationId":"mePt.cancel","summary":"DELETE /me/pt/bookings/{booking} \u2014 cancel + refund the reserved credit","tags":["Member App (self-service)"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"cancelled"}},"required":["status"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/profile":{"get":{"operationId":"meProfile.show","summary":"GET /me/profile \u2014 the member's own profile","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"first_name_ar":{"type":["string","null"]},"last_name_ar":{"type":["string","null"]},"avatar_url":{"type":"null"},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"address":{"type":["string","null"]},"home_club_id":{"type":["string","null"]},"home_club_name":{},"national_id":{"type":["string","null"]},"dob":{"type":"string"},"gender":{"type":"string"},"language_pref":{"type":"string"},"weight_unit":{"type":["string","null"]},"emergency_contact_name":{"type":["string","null"]},"emergency_contact_phone":{"type":["string","null"]},"emergency_contact_relation":{"type":["string","null"]}},"required":["first_name","last_name","first_name_ar","last_name_ar","avatar_url","email","mobile","address","home_club_id","home_club_name","national_id","dob","gender","language_pref","weight_unit","emergency_contact_name","emergency_contact_phone","emergency_contact_relation"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"meProfile.update","summary":"PATCH /me/profile \u2014 update editable fields (email/mobile changes need a one-time code)","tags":["Member App (self-service)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","maxLength":120},"last_name":{"type":"string","maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"email":{"type":["string","null"],"format":"email","maxLength":190},"mobile":{"type":["string","null"],"maxLength":30},"address":{"type":["string","null"],"maxLength":255},"language_pref":{"type":"string","enum":["ar","en"]},"weight_unit":{"type":["string","null"],"enum":["kg","lb"]},"emergency_contact_name":{"type":["string","null"],"maxLength":190},"emergency_contact_phone":{"type":["string","null"],"maxLength":30},"emergency_contact_relation":{"type":["string","null"],"maxLength":60},"otp":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"first_name_ar":{"type":["string","null"]},"last_name_ar":{"type":["string","null"]},"avatar_url":{"type":"null"},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"address":{"type":["string","null"]},"home_club_id":{"type":["string","null"]},"home_club_name":{},"national_id":{"type":["string","null"]},"dob":{"type":"string"},"gender":{"type":"string"},"language_pref":{"type":"string"},"weight_unit":{"type":["string","null"]},"emergency_contact_name":{"type":["string","null"]},"emergency_contact_phone":{"type":["string","null"]},"emergency_contact_relation":{"type":["string","null"]}},"required":["first_name","last_name","first_name_ar","last_name_ar","avatar_url","email","mobile","address","home_club_id","home_club_name","national_id","dob","gender","language_pref","weight_unit","emergency_contact_name","emergency_contact_phone","emergency_contact_relation"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/language":{"put":{"operationId":"meProfile.language","description":"A dedicated, single-purpose endpoint the app calls whenever the user flips the language toggle:\nit's the source of truth for which language to send *push* notifications in. Notifications are\nstored bilingually, and {@see SendPushNotification} reads `language_pref` to pick the\ntitle/body \u2014 so a member who switches to Arabic starts getting Arabic pushes immediately.\n(In-app notifications carry both languages and are chosen client-side.) No OTP, no other fields.","summary":"PUT /me/language \u2014 persist the member's language choice (ar|en)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"language":{"type":"string","enum":["ar","en"]}},"required":["language"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"language_pref":{"type":"string"}},"required":["language_pref"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/home-club":{"put":{"operationId":"meProfile.homeClub","summary":"PUT /me/home-club \u2014 change the member's default/home branch. Pick from GET /public/branches.\nThe new branch becomes the default for the online membership purchase (RFP 09)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"}},"required":["club_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"home_club_id":{"type":["string","null"]},"home_club_name":{}},"required":["home_club_id","home_club_name"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/profile/avatar":{"post":{"operationId":"meProfile.avatar","summary":"POST /me/profile/avatar \u2014 upload a member-app profile picture as a base64 data URL\n({@code data:image/jpeg;base64,\u2026}). Stored on the public disk; any previous avatar is replaced.\nThis is a self-chosen avatar, distinct from the approval-gated identity {@code photo}. Returns the URL","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"image":{"type":"string"}},"required":["image"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"avatar_url":{"type":"null"}},"required":["avatar_url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/profile/change-request":{"post":{"operationId":"meProfile.changeRequest","summary":"POST /me/profile/change-request \u2014 request a back-office change to a locked identity field","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"field":{"type":"string","enum":[""]},"requested_value":{"type":"string","maxLength":190},"reason":{"type":["string","null"],"maxLength":500}},"required":["field","requested_value"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/profile/change-requests":{"get":{"operationId":"meProfile.changeRequests","summary":"GET /me/profile/change-requests \u2014 the status of the member's own change requests","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"field":{"type":["string","null"]},"requested_value":{"type":["string","null"]},"status":{"type":"string"},"requested_at":{"type":"string"}},"required":["id","field","requested_value","status","requested_at"]}}},"required":["requests"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/security/otp":{"post":{"operationId":"meSecurity.requestOtp","summary":"POST /me/security/otp \u2014 email a one-time code for a sensitive action","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"purpose":{"type":"string","enum":["profile_contact","wallet","refund"]}},"required":["purpose"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"},"debug_code":{"type":["string","null"]}},"required":["sent","debug_code"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/password":{"post":{"operationId":"meSecurity.setPassword","summary":"POST /me/password \u2014 set or change the member's own password (RFP 24 #40). A member who joined\nonline has no password; once signed in (via the activation one-time code) they create one here.\nComplexity is enforced; other sessions are invalidated on success","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"password":{"type":"string","minLength":8},"password_confirmation":{"type":"string","minLength":8}},"required":["password","password_confirmation"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"boolean"}},"required":["updated"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/lockers":{"get":{"operationId":"meLocker.index","summary":"GET /me/lockers \u2014 the member's current locker (if any) + the lockers available to rent","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"current":{"type":["object","null"],"properties":{"assignment_id":{"type":"string"},"locker_id":{"type":"string"},"code":{"type":"string"},"size":{"type":"string"},"club_name":{"type":["string","null"]},"monthly_fee_minor":{"type":"integer"},"assigned_at":{"type":"string"}},"required":["assignment_id","locker_id","code","size","club_name","monthly_fee_minor","assigned_at"]},"available":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"size":{"type":"string"},"club_name":{"type":["string","null"]},"monthly_fee_minor":{"type":"integer"}},"required":["id","code","size","club_name","monthly_fee_minor"]}},"wallet_balance_minor":{"type":"integer"}},"required":["current","available","wallet_balance_minor"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"meLocker.release","summary":"DELETE /me/lockers \u2014 release the member's own locker","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"released"}},"required":["status"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"You don't have a locker to release."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/lockers/{locker}/rent":{"post":{"operationId":"meLocker.rent","summary":"POST /me/lockers/{locker}/rent \u2014 rent a locker, paying the monthly fee from the wallet","tags":["Member App (self-service)"],"parameters":[{"name":"locker","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"assignment_id":{"type":"string"},"status":{"type":"string","const":"rented"}},"required":["assignment_id","status"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"message":{"type":"string","const":"Not enough wallet balance \u2014 top up your wallet first."}},"required":["message"]},{"type":"object","properties":{"message":{"type":"string","const":"This locker is not available for rent."}},"required":["message"]},{"type":"object","properties":{"message":{"type":"string","const":"That locker is no longer available."}},"required":["message"]},{"type":"object","properties":{"message":{"type":"string","const":"You already have a locker. Release it before renting another."}},"required":["message"]}]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/documents":{"get":{"operationId":"meDocument.index","summary":"GET /me/documents \u2014 the member's uploaded documents","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"original_name":{"type":"string"},"status":{"type":"string"},"size":{"type":"integer"},"expiry_date":{"type":"string"},"uploaded_at":{"type":"string"},"download_url":{"type":"string","description":"Signed, token-less download link \u2014 opens directly in a browser, expires in 24h, and\ncan't be forged or enumerated (the signature covers the document id + expiry)."}},"required":["id","type","original_name","status","size","expiry_date","uploaded_at","download_url"]}}},"required":["documents"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meDocument.store","summary":"POST /me/documents \u2014 upload a document (PDF / image, \u22648 MB)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":[""]},"file":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","maxLength":8192},"expiry_date":{"type":["string","null"],"format":"date-time"}},"required":["type","file"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/documents/{document}":{"delete":{"operationId":"meDocument.destroy","summary":"DELETE /me/documents/{document} \u2014 remove a document still awaiting review","tags":["Member App (self-service)"],"parameters":[{"name":"document","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Approved documents cannot be removed."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/health-waiver":{"get":{"operationId":"meHealthWaiver.show","summary":"GET /me/health-waiver \u2014 the member's waiver status + their latest uploaded copy (if any)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"},"at":{"type":"string"},"document":{"type":["object","null"],"properties":{"id":{"type":"string"},"original_name":{"type":"string"},"status":{"type":"string"},"uploaded_at":{"type":"string"},"download_url":{"type":"string"}},"required":["id","original_name","status","uploaded_at","download_url"]}},"required":["received","at","document"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meHealthWaiver.store","summary":"POST /me/health-waiver \u2014 upload a signed PAR-Q (PDF / image, \u22648 MB); marks the waiver received","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","maxLength":8192}},"required":["file"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"},"at":{"type":"string"},"document":{"type":["object","null"],"properties":{"id":{"type":"string"},"original_name":{"type":"string"},"status":{"type":"string"},"uploaded_at":{"type":"string"},"download_url":{"type":"string"}},"required":["id","original_name","status","uploaded_at","download_url"]}},"required":["received","at","document"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/addons":{"get":{"operationId":"meAddon.index","summary":"GET /me/addons \u2014 current grants + active buyable add-ons + the member's wallet balance","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"granted":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"image_url":{"type":"string"},"kind":{"type":"string"},"credits_remaining":{"type":["integer","null"]},"credits_total":{"type":["integer","null"]},"expires_at":{"type":"string"},"status":{"type":"string"}},"required":["id","name","name_ar","image_url","kind","credits_remaining","credits_total","expires_at","status"]}},"available":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"kind":{"type":"string"},"credits":{"type":["integer","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"}},"required":["id","name","name_ar","image_url","kind","credits","price_minor","currency"]}},"bundles":{"type":"array","description":"Bundles (\"programs\") \u2014 a discounted set of add-ons bought in one go (RFP 02).","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"components_value_minor":{"type":"integer","description":"The sum of the component add-ons' list prices \u2014 so the app can show the saving."},"items":{"type":"array","items":{"type":"object","properties":{"add_on_id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"kind":{"type":"string"},"quantity":{"type":"integer"}},"required":["add_on_id","name","name_ar","kind","quantity"]}}},"required":["id","name","name_ar","price_minor","currency","components_value_minor","items"]}},"wallet_balance_minor":{"type":"integer"}},"required":["granted","available","bundles","wallet_balance_minor"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/addons/purchase":{"post":{"operationId":"meAddon.purchase","summary":"POST /me/addons/purchase { add_on_id | bundle_id, method?, gateway?, coupon_code? } \u2014 buy a\nsingle add-on or a bundle (\"program\") with wallet (instant) or card/BNPL (hosted redirect; the\ngrant is created when the charge settles). An optional cart coupon discounts the amount","tags":["Member App (self-service)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"add_on_id":{"type":["string","null"]},"bundle_id":{"type":["string","null"]},"method":{"type":["string","null"],"enum":["wallet","card"]},"gateway":{"type":["string","null"],"description":"For a hosted-page payment, the chosen gateway (card processor or BNPL) the brand has enabled."},"coupon_code":{"type":["string","null"],"maxLength":60}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"grant_id":{"type":"string"},"discount_minor":{"type":"string"},"wallet_balance_minor":{"type":"integer"}},"required":["grant_id","discount_minor","wallet_balance_minor"]},{"type":"object","properties":{"payment_id":{"type":"string"},"redirect_url":{"type":["string","null"]},"discount_minor":{"type":"string"}},"required":["payment_id","redirect_url","discount_minor"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/coupons/validate":{"post":{"operationId":"meCoupon.check","summary":"POST /me/coupons/validate { code, amount_minor } \u2192 { valid, discount_minor?, coupon_id?, message? }","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":60},"amount_minor":{"type":"integer","minimum":1}},"required":["code","amount_minor"]}}}},"responses":{"200":{"description":"A bad/expired/inapplicable code is not an error here \u2014 it's a \"no, and here's why\".","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"valid":{"type":"boolean"},"discount_minor":{"type":"integer"},"coupon_id":{"type":"string"}},"required":["valid","discount_minor","coupon_id"]},{"type":"object","properties":{"valid":{"type":"boolean"},"message":{"type":"string"}},"required":["valid","message"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/vouchers":{"get":{"operationId":"meCoupon.vouchers","summary":"GET /me/vouchers \u2014 the member's personal vouchers (single-use coupons issued to them,\ne.g. as a challenge reward). Shows unused + still-valid first; each carries its code,\ndiscount and expiry so the member can use it at the till / checkout","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"vouchers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"discount_type":{"type":"string"},"amount":{"type":"integer"},"valid_to":{"type":"string","description":"percent, or halalas for fixed"},"used":{"type":"boolean"},"expired":{"type":"string"},"redeemable":{"type":"string"}},"required":["id","code","name","name_ar","discount_type","amount","valid_to","used","expired","redeemable"]}}},"required":["vouchers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/shop":{"get":{"operationId":"meShop.index","summary":"GET /me/shop?club_id= \u2014 active products (+ stock at the chosen branch) and the wallet balance","tags":["Member App (self-service)"],"parameters":[{"name":"club_id","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sku":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"category":{"type":["string","null"]},"description":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"in_stock":{"type":["boolean","null"]},"stock":{"type":["integer","null"]}},"required":["id","sku","name","name_ar","image_url","category","description","price_minor","currency","in_stock","stock"]}},"categories":{"type":"array","items":{}},"wallet_balance_minor":{"type":"integer"}},"required":["products","categories","wallet_balance_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/shop/orders":{"get":{"operationId":"meShop.orders","summary":"GET /me/shop/orders \u2014 the member's order history, newest first, with the sale's receipt lines","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"orders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":["string","null"]},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"created_at":{"type":"string"},"items":{"type":"array","items":{}},"lines":{"type":"string","description":"Receipt lines once fulfilled (name/qty/VAT split from the retail sale)."}},"required":["id","status","club_id","club_name","amount_minor","currency","created_at","items","lines"]}}},"required":["orders"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meShop.store","summary":"POST /me/shop/orders { club_id, items: [{product_id, quantity}], method?, gateway? } \u2014 buy a\nbasket for pickup at the chosen branch. Wallet pays and fulfils instantly; card returns a\nhosted redirect_url and the order fulfils on settle","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"method":{"type":["string","null"],"enum":["wallet","card"]},"gateway":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":"string"},"quantity":{"type":"integer","minimum":1,"maximum":20}},"required":["product_id","quantity"]},"minItems":1}},"required":["club_id","items"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"order_id":{"type":"string"},"status":{"type":"string"},"amount_minor":{"type":"string"},"wallet_balance_minor":{"type":"integer"}},"required":["order_id","status","amount_minor","wallet_balance_minor"]},{"type":"object","properties":{"order_id":{"type":"string"},"payment_id":{"type":"string"},"redirect_url":{"type":["string","null"]}},"required":["order_id","payment_id","redirect_url"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/code":{"get":{"operationId":"social.code","summary":"GET /me/social/code \u2014 my stable friend code (minted on first read)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/friends":{"get":{"operationId":"social.friends","summary":"GET /me/social/friends \u2014 my confirmed friends (+ live streak / at-club signal)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"friends":{"type":"array","items":{"type":["object","null"],"properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},"active_challenges":{"type":"integer"},"streak_days":{"type":"integer"},"at_club_now":{"type":"boolean"}},"required":["member","active_challenges","streak_days","at_club_now"]}}},"required":["friends"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/friends/{memberId}":{"delete":{"operationId":"social.removeFriend","summary":"DELETE /me/social/friends/{member} \u2014 remove a friend (both sides)","tags":["Member App (self-service)"],"parameters":[{"name":"memberId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/friends/{memberId}/block":{"post":{"operationId":"social.block","summary":"POST /me/social/friends/{member}/block \u2014 remove + prevent re-adding (silent both ways)","tags":["Member App (self-service)"],"parameters":[{"name":"memberId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/requests":{"get":{"operationId":"social.requests","summary":"GET /me/social/requests \u2014 pending friend requests, both directions","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":["object","null"],"properties":{"id":{"type":"string"},"direction":{"type":"string","enum":["incoming","outgoing"]},"created_at":{"type":"string"},"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}},"required":["id","direction","created_at","member"]}}},"required":["requests"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"social.sendRequest","summary":"POST /me/social/requests {code | member_id} \u2014 send a friend request by code or (community v2) member id","tags":["Member App (self-service)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":["string","null"],"maxLength":20},"member_id":{"type":["string","null"]}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"request":{"type":["object","null"],"properties":{"id":{"type":"string"},"direction":{"type":"string","enum":["incoming","outgoing"]},"created_at":{"type":"string"},"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}},"required":["id","direction","created_at","member"]}},"required":["request"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/requests/{requestId}/accept":{"post":{"operationId":"social.acceptRequest","summary":"POST /me/social/requests/{request}/accept \u2014 accept an incoming request","tags":["Member App (self-service)"],"parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"friend":{"type":"object","properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},"active_challenges":{"type":"integer"},"streak_days":{"type":"integer"},"at_club_now":{"type":"boolean"}},"required":["member","active_challenges","streak_days","at_club_now"]}},"required":["friend"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/requests/{requestId}":{"delete":{"operationId":"social.deleteRequest","summary":"DELETE /me/social/requests/{request} \u2014 decline (incoming) or cancel (outgoing)","tags":["Member App (self-service)"],"parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/challenges":{"get":{"operationId":"social.challenges","summary":"GET /me/social/challenges \u2014 all my challenges (active + pending + recent history)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"participants":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"score":{"type":"integer"},"accepted":{"type":"boolean"}},"required":["member","score","accepted"]}}},"required":["id","metric","status","duration_days","starts_at","ends_at","creator","participants"]}}},"required":["challenges"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"social.createChallenge","summary":"POST /me/social/challenges {metric, duration_days, friend_ids[]} \u2014 create + invite","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"metric":{"type":"string","maxLength":40},"duration_days":{"type":"integer","minimum":1,"maximum":30},"friend_ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":9}},"required":["metric","duration_days","friend_ids"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"participants":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"score":{"type":"integer"},"accepted":{"type":"boolean"}},"required":["member","score","accepted"]}}},"required":["id","metric","status","duration_days","starts_at","ends_at","creator","participants"]}},"required":["challenge"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/challenges/{challengeId}":{"get":{"operationId":"social.challenge","summary":"GET /me/social/challenges/{challenge} \u2014 one challenge (deep-link / live refresh)","tags":["Member App (self-service)"],"parameters":[{"name":"challengeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"participants":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"score":{"type":"integer"},"accepted":{"type":"boolean"}},"required":["member","score","accepted"]}}},"required":["id","metric","status","duration_days","starts_at","ends_at","creator","participants"]}},"required":["challenge"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"social.leaveChallenge","summary":"DELETE /me/social/challenges/{challenge} \u2014 leave (active) or cancel (creator + pending)","tags":["Member App (self-service)"],"parameters":[{"name":"challengeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/challenges/{challengeId}/accept":{"post":{"operationId":"social.acceptChallenge","summary":"POST /me/social/challenges/{challenge}/accept \u2014 accept an invite (first accept starts it)","tags":["Member App (self-service)"],"parameters":[{"name":"challengeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"participants":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"score":{"type":"integer"},"accepted":{"type":"boolean"}},"required":["member","score","accepted"]}}},"required":["id","metric","status","duration_days","starts_at","ends_at","creator","participants"]}},"required":["challenge"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/challenges/{challengeId}/decline":{"post":{"operationId":"social.declineChallenge","summary":"POST /me/social/challenges/{challenge}/decline \u2014 decline an invite (silent)","tags":["Member App (self-service)"],"parameters":[{"name":"challengeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/challenges/{challengeId}/rematch":{"post":{"operationId":"social.rematchChallenge","summary":"POST /me/social/challenges/{challenge}/rematch \u2014 clone a finished challenge, re-inviting everyone","tags":["Member App (self-service)"],"parameters":[{"name":"challengeId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"challenge":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"participants":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"score":{"type":"integer"},"accepted":{"type":"boolean"}},"required":["member","score","accepted"]}}},"required":["id","metric","status","duration_days","starts_at","ends_at","creator","participants"]}},"required":["challenge"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/social/nudges":{"post":{"operationId":"social.nudge","summary":"POST /me/social/nudges {member_id, challenge_id?} \u2014 \u00abyalla, train \ud83d\udc4a\u00bb push to a friend","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"challenge_id":{"type":["string","null"]}},"required":["member_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/referrals":{"get":{"operationId":"meReferral.index","summary":"GET /me/referrals \u2014 the member's invite code (minted if missing), share url + their referrals","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"share_url":{"type":"string"},"referrals":{"type":"array","items":{"type":"object","properties":{"name_masked":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["name_masked","status","created_at"]}},"total":{"type":"integer","minimum":0}},"required":["code","share_url","referrals","total"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/referrals/summary":{"get":{"operationId":"meReferral.summary","summary":"GET /me/referrals/summary \u2014 the reward detail the member app's points/referral card needs:\nthe invite code (minted on first read), the per-friend reward (halalas), and how many friends\nthis member has invited (attributed) vs. how many have paid out","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"reward_per_friend_minor":{"type":"integer"},"invited":{"type":"integer","minimum":0},"rewarded":{"type":"integer","minimum":0}},"required":["code","reward_per_friend_minor","invited","rewarded"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/referrals/apply":{"post":{"operationId":"meReferral.apply","summary":"POST /me/referrals/apply \u2014 the current member (as referee) applies a friend's code. Attribution\nonly; the reward is paid to the referrer's wallet later, when this member's membership activates\n(RewardReferralOnContractActivated). Guards against using your own code or applying twice","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"applied":{"type":"boolean"},"message":{"type":"object","properties":{"ar":{"type":"string","const":"\u062a\u0645 \u062a\u0637\u0628\u064a\u0642 \u0631\u0645\u0632 \u0627\u0644\u0625\u062d\u0627\u0644\u0629"},"en":{"type":"string","const":"Referral code applied"}},"required":["ar","en"]}},"required":["applied","message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/free-visits":{"get":{"operationId":"meFreeVisit.index","summary":"GET /me/free-visits \u2014 the acting member's trial visits, newest first","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"visits":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"scheduled_at":{"type":"string"},"status":{"type":"string"},"visit_number":{"type":"integer","description":"booked | showed | no_show | cancelled"},"host_name":{"type":["string","null"]},"branch":{},"notes":{"type":["string","null"]}},"required":["id","scheduled_at","status","visit_number","host_name","branch","notes"]}}},"required":["visits"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/notifications":{"get":{"operationId":"meNotification.index","summary":"GET /me/notifications \u2014 newest first + the unread count for the bell badge","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"category":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"body":{"type":["string","null"]},"body_ar":{"type":["string","null"]},"read":{"type":"boolean"},"created_at":{"type":"string"},"target":{"type":"object","description":"Deep-link payload the app tapped notification routes to: the screen to open (derived\nfrom type/category) plus any entity ids the producer attached, and the raw data.","additionalProperties":{}},"data":{"type":["array","null"],"items":{}}},"required":["id","category","type","title","title_ar","body","body_ar","read","created_at","target","data"]}},"unread_count":{"type":"integer","minimum":0}},"required":["notifications","unread_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/notifications/read-all":{"post":{"operationId":"meNotification.markAllRead","summary":"POST /me/notifications/read-all \u2014 clear the badge","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"unread_count":{"type":"integer"}},"required":["unread_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/notifications/{notification}/read":{"post":{"operationId":"meNotification.markRead","summary":"POST /me/notifications/{notification}/read \u2014 mark one of the member's notifications read","tags":["Member App (self-service)"],"parameters":[{"name":"notification","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"unread_count":{"type":"integer","minimum":0}},"required":["unread_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/notification-preferences":{"get":{"operationId":"meNotification.preferences","summary":"GET /me/notification-preferences \u2014 per-category push/email opt-in (default on)","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"array","items":{}}},"required":["preferences"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"meNotification.updatePreferences","summary":"PUT /me/notification-preferences \u2014 upsert the member's per-category channel opt-ins","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"push":{"type":"boolean"},"email":{"type":"boolean"}},"required":["category","push","email"]},"minItems":1}},"required":["preferences"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"array","items":{}}},"required":["preferences"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/notifications/device":{"post":{"operationId":"meNotification.registerDevice","summary":"POST /me/notifications/device \u2014 register (or refresh) this device's FCM token for push.\nA token is unique within a brand, so re-registering re-points it to the current member","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","maxLength":512},"platform":{"type":["string","null"],"enum":["android","ios","web"]},"app":{"type":["string","null"],"maxLength":20}},"required":["token"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/notifications/device/{token}":{"delete":{"operationId":"meNotification.unregisterDevice","summary":"DELETE /me/notifications/device/{token} \u2014 unregister a device (logout)","tags":["Member App (self-service)"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/transactions":{"get":{"operationId":"me.transactions","summary":"GET /me/transactions (RFP 23 #51) \u2014 the member's payment history: charges made directly by the\nmember plus those against any of their contracts (in-club sales), newest first","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"transactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"method":{"type":["string","null"]},"gateway":{"type":["string","null"]},"purpose":{"type":["string","null"]},"created_at":{"type":"string"},"settled_at":{"type":"string"}},"required":["id","amount_minor","currency","status","method","gateway","purpose","created_at","settled_at"]}}},"required":["transactions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/agreements":{"get":{"operationId":"meAgreement.index","summary":"GET /me/agreements \u2014 the member's agreements with sign/download state","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"agreements":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"membership_agreement"},"language":{"type":"string"},"status":{"type":"string"},"is_signed":{"type":"boolean"},"issued_at":{"type":"string"},"signed_at":{"type":"string"},"download_url":{"type":"string"}},"required":["id","type","language","status","is_signed","issued_at","signed_at","download_url"]}}},"required":["agreements"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/agreements/{agreement}/download":{"get":{"operationId":"meAgreement.download","summary":"GET /me/agreements/{agreement}/download \u2014 stream the member's own agreement PDF","tags":["Member App (self-service)"],"parameters":[{"name":"agreement","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/agreements/{agreement}/sign":{"post":{"operationId":"meAgreement.sign","summary":"POST /me/agreements/{agreement}/sign \u2014 capture the member's on-screen signature (a PNG data\nURL), store it on the private agreements disk, and mark the agreement Signed (RFP 24 #7)","tags":["Member App (self-service)"],"parameters":[{"name":"agreement","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"signature":{"type":"string"}},"required":["signature"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"signed"},"signed_at":{"type":"string"}},"required":["status","signed_at"]}}}},"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"signed"},"signed_at":{"type":"string"}},"required":["status","signed_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/account/deletion":{"get":{"operationId":"meAccount.deletionStatus","summary":"GET /me/account/deletion \u2014 the current request status + any blockers stopping a new one","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"anyOf":[{"type":"string"},{"type":"string","enum":["none"]}]},"requested_at":{"type":"string"},"blockers":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"code":{"type":"string","const":"active_membership"},"message":{"type":"string","const":"You have an active membership \u2014 please cancel it first."}},"required":["code","message"]},{"type":"object","properties":{"code":{"type":"string","const":"outstanding_payment"},"message":{"type":"string","const":"You have a pending payment \u2014 please settle or cancel it first."}},"required":["code","message"]}]}}},"required":["status","requested_at","blockers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"meAccount.requestDeletion","summary":"POST /me/account/deletion \u2014 submit a PDPL erasure request (422 with reasons if blocked)","tags":["Member App (self-service)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":["string","null"],"maxLength":500}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"pending"},"requested_at":{"type":"string"}},"required":["status","requested_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"meAccount.cancelDeletion","summary":"DELETE /me/account/deletion \u2014 withdraw a pending request","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"cancelled"}},"required":["status"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/account/export":{"get":{"operationId":"meAccount.export","summary":"GET /me/account/export \u2014 PDPL data portability: the member's own data as a JSON download","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"exported_at":{"type":"string"},"profile":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"first_name_ar":{"type":"string"},"last_name_ar":{"type":"string"},"email":{"type":"string"},"mobile":{"type":"string"},"national_id":{"type":"string"},"gender":{"type":"string"},"dob":{"type":"string"},"language_pref":{"type":"string"}},"required":["first_name","last_name","first_name_ar","last_name_ar","email","mobile","national_id","gender","dob","language_pref"]},"memberships":{"type":"array","items":{"type":"object","properties":{"plan":{"type":"string"},"state":{"type":"string"},"activation_date":{"type":"string"},"expiry_date":{"type":"string"},"amount_minor":{"type":"integer"}},"required":["plan","state","activation_date","expiry_date","amount_minor"]}},"transactions":{"type":"array","items":{"type":"object","properties":{"amount_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"purpose":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["amount_minor","currency","status","purpose","created_at"]}},"add_ons":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"credits_remaining":{"type":"string"},"status":{"type":"string"}},"required":["name","credits_remaining","status"]}},"bookings":{"type":"array","description":"Read raw so a legacy out-of-enum status (e.g. 'attended') can't 500 the PDPL export.","items":{"type":"object","properties":{"session_id":{"type":"string"},"status":{"type":"string"}},"required":["session_id","status"]}},"wallet":{"type":"object","properties":{"balance_minor":{"type":"integer"}},"required":["balance_minor"]},"notifications":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"created_at":{"type":"string"}},"required":["type","title","created_at"]}}},"required":["exported_at","profile","memberships","transactions","add_ons","bookings","wallet","notifications"]}}},"headers":{"Content-Disposition":{"schema":{"type":"string","const":"attachment; filename=\"memvera-data-export.json\""}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/today":{"get":{"operationId":"trainerToday.index","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"greeting":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"date":{"type":"string"},"date_hijri":{"type":["object","null"],"properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"kpis":{"type":"object","properties":{"sessions_today":{"type":"integer","minimum":0},"pt_clients":{"type":"integer","minimum":0},"work_hours":{"type":"integer"},"rating":{"type":["number","null"]}},"required":["sessions_today","pt_clients","work_hours","rating"]},"now_marker":{"type":"string"},"timeline":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["pt","class"]},"title":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"subtitle":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"duration_min":{"type":"integer"},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]},"attendance":{"type":["object","null"],"properties":{"booked":{"type":"integer","minimum":0},"capacity":{"type":"integer"}},"required":["booked","capacity"]},"waitlist":{"type":"integer","minimum":0},"pt_session_id":{"type":["string","null"]}},"required":["id","kind","title","subtitle","start","duration_min","status","attendance","waitlist","pt_session_id"]}}},"required":["greeting","date","date_hijri","kpis","now_marker","timeline"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/schedule":{"get":{"operationId":"trainerSchedule.index","tags":["Member App (self-service)"],"parameters":[{"name":"range","in":"query","schema":{"type":["string","null"],"enum":["day","week","month"]}},{"name":"date","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string"},"date_hijri":{"type":["object","null"],"properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"now_marker":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","enum":["pt","class"]},"title":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"subtitle":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"duration_min":{"type":"integer"},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]},"attendance":{"type":["object","null"],"properties":{"booked":{"type":"integer","minimum":0},"capacity":{"type":"integer"}},"required":["booked","capacity"]},"waitlist":{"type":"integer","minimum":0},"pt_session_id":{"type":["string","null"]}},"required":["id","kind","title","subtitle","start","duration_min","status","attendance","waitlist","pt_session_id"]}}},"required":["date","date_hijri","now_marker","items"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/roster":{"get":{"operationId":"trainerClassSession.roster","summary":"GET /me/trainer/sessions/{session}/roster \u2014 session metadata + the booked roster (waitlist flagged)","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string","const":"class"},"title":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"class_type":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"location":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"end":{"type":"string"},"duration_min":{"type":"integer"},"capacity":{"type":"integer"},"booked":{"type":"integer","minimum":0},"waitlist":{"type":"integer","minimum":0},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]}},"required":["id","kind","title","class_type","location","start","end","duration_min","capacity","booked","waitlist","status"]},"roster":{"type":"array","items":{"type":"object","properties":{"booking_id":{"type":"string"},"member":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"}},"required":["id","name","initial"]},"status":{"type":"string"},"waitlisted":{"type":"boolean"}},"required":["booking_id","member","status","waitlisted"]}}},"required":["session","roster"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/attendance":{"post":{"operationId":"trainerClassSession.attendance","summary":"POST /me/trainer/sessions/{session}/attendance {booking_id, outcome: attended|no_show} \u2014 mark one\nmember. \"attended\" maps to the backend's \"show\" (conducts the booking + emits commission)","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"},"outcome":{"type":"string","enum":["attended","no_show"]}},"required":["booking_id","outcome"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"},"status":{"type":"string"}},"required":["booking_id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/conduct":{"post":{"operationId":"trainerClassSession.conduct","summary":"POST /me/trainer/sessions/{session}/conduct {exceptions?: [booking ids]} \u2014 bulk close: everyone\nwith a check-in \u2192 attended, the flagged exceptions \u2192 manager override, the rest \u2192 no-show","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"exceptions":{"type":["array","null"],"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"conducted":{"type":"integer"},"no_show":{"type":"integer"},"overrides":{"type":"integer"}},"required":["conducted","no_show","overrides"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}":{"get":{"operationId":"trainerPtSession.show","summary":"GET /me/trainer/sessions/{session} \u2014 the full PT sheet","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"}},"required":["id","name","initial"]},"focus":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"duration_min":{"type":"integer"},"location":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"package":{"type":["object","null"],"properties":{"used":{"type":"integer"},"total":{"type":"integer"}},"required":["used","total"]},"program":{"type":["object","null"],"properties":{"id":{"type":"string"},"ref":{"type":"object","properties":{"ar":{"anyOf":[{"type":"string"},{"type":"null"}]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","ref"]},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]},"live_started_at":{"type":"string"},"rest_sec":{"type":"integer","const":90}},"required":["id","client","focus","duration_min","location","start","package","program","status","live_started_at","rest_sec"]}},"required":["session"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/start":{"post":{"operationId":"trainerPtSession.start","summary":"POST /me/trainer/sessions/{session}/start \u2014 anchor the live timer","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"}},"required":["id","name","initial"]},"focus":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"duration_min":{"type":"integer"},"location":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"package":{"type":["object","null"],"properties":{"used":{"type":"integer"},"total":{"type":"integer"}},"required":["used","total"]},"program":{"type":["object","null"],"properties":{"id":{"type":"string"},"ref":{"type":"object","properties":{"ar":{"anyOf":[{"type":"string"},{"type":"null"}]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","ref"]},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]},"live_started_at":{"type":"string"},"rest_sec":{"type":"integer","const":90}},"required":["id","client","focus","duration_min","location","start","package","program","status","live_started_at","rest_sec"]}},"required":["session"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Session is cancelled."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/complete":{"post":{"operationId":"trainerPtSession.complete","summary":"POST /me/trainer/sessions/{session}/complete \u2014 trainer-confirm delivery (two-key)","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"}},"required":["id","name","initial"]},"focus":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"duration_min":{"type":"integer"},"location":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"package":{"type":["object","null"],"properties":{"used":{"type":"integer"},"total":{"type":"integer"}},"required":["used","total"]},"program":{"type":["object","null"],"properties":{"id":{"type":"string"},"ref":{"type":"object","properties":{"ar":{"anyOf":[{"type":"string"},{"type":"null"}]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","ref"]},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]},"live_started_at":{"type":"string"},"rest_sec":{"type":"integer","const":90}},"required":["id","client","focus","duration_min","location","start","package","program","status","live_started_at","rest_sec"]}},"required":["session"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"message":{"type":"string","enum":["The member has a gate check-in for this session \u2014 a manager must review this no-show.","No member check-in found \u2014 a Fitness Manager must approve this attendance."]},"conflict":{"type":"boolean"},"approval_id":{"type":"string"}},"required":["message","conflict","approval_id"]},{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},{"type":"object","properties":{"message":{"type":"string","const":"This attendance is under manager review."}},"required":["message"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/no-show":{"post":{"operationId":"trainerPtSession.noShow","summary":"POST /me/trainer/sessions/{session}/no-show \u2014 member absent; reserved credit forfeits","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"}},"required":["id","name","initial"]},"focus":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"duration_min":{"type":"integer"},"location":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"package":{"type":["object","null"],"properties":{"used":{"type":"integer"},"total":{"type":"integer"}},"required":["used","total"]},"program":{"type":["object","null"],"properties":{"id":{"type":"string"},"ref":{"type":"object","properties":{"ar":{"anyOf":[{"type":"string"},{"type":"null"}]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","ref"]},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]},"live_started_at":{"type":"string"},"rest_sec":{"type":"integer","const":90}},"required":["id","client","focus","duration_min","location","start","package","program","status","live_started_at","rest_sec"]}},"required":["session"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"message":{"type":"string","enum":["The member has a gate check-in for this session \u2014 a manager must review this no-show.","No member check-in found \u2014 a Fitness Manager must approve this attendance."]},"conflict":{"type":"boolean"},"approval_id":{"type":"string"}},"required":["message","conflict","approval_id"]},{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},{"type":"object","properties":{"message":{"type":"string","const":"This attendance is under manager review."}},"required":["message"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/reschedule":{"post":{"operationId":"trainerPtSession.reschedule","summary":"POST /me/trainer/sessions/{session}/reschedule {new_start} \u2014 move the session","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"new_start":{"type":"string","format":"date-time"}},"required":["new_start"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"}},"required":["id","name","initial"]},"focus":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"duration_min":{"type":"integer"},"location":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"package":{"type":["object","null"],"properties":{"used":{"type":"integer"},"total":{"type":"integer"}},"required":["used","total"]},"program":{"type":["object","null"],"properties":{"id":{"type":"string"},"ref":{"type":"object","properties":{"ar":{"anyOf":[{"type":"string"},{"type":"null"}]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","ref"]},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]},"live_started_at":{"type":"string"},"rest_sec":{"type":"integer","const":90}},"required":["id","client","focus","duration_min","location","start","package","program","status","live_started_at","rest_sec"]}},"required":["session"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/cancel":{"post":{"operationId":"trainerPtSession.cancel","summary":"POST /me/trainer/sessions/{session}/cancel \u2014 trainer-initiated: the member is never\npenalised (credit always refunds; `late` in the body is informational only)","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"}},"required":["id","name","initial"]},"focus":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"duration_min":{"type":"integer"},"location":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"start":{"type":"string"},"package":{"type":["object","null"],"properties":{"used":{"type":"integer"},"total":{"type":"integer"}},"required":["used","total"]},"program":{"type":["object","null"],"properties":{"id":{"type":"string"},"ref":{"type":"object","properties":{"ar":{"anyOf":[{"type":"string"},{"type":"null"}]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","ref"]},"status":{"type":"string","enum":["upcoming","due","live","ended","no_show","cancelled"]},"live_started_at":{"type":"string"},"rest_sec":{"type":"integer","const":90}},"required":["id","client","focus","duration_min","location","start","package","program","status","live_started_at","rest_sec"]},{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","const":"cancelled"}},"required":["id","status"]}]}},"required":["session"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"No booking under this session."}},"required":["message"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/sessions/{session}/rating-request":{"post":{"operationId":"trainerPtSession.ratingRequest","summary":"POST /me/trainer/sessions/{session}/rating-request \u2014 nudge the member to rate","tags":["Member App (self-service)"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Member push/notification wiring lands with the member-notification centre (Wave 4).","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"}},"required":["sent"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/notifications":{"get":{"operationId":"trainerNotification.index","summary":"GET /me/trainer/notifications","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"anyOf":[{"type":"string"},{"type":"string","enum":["schedule_change"]}]},"group":{"type":"string","enum":["today","yesterday","earlier"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"body":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"created_at":{"type":"string"},"read":{"type":"boolean"}},"required":["id","kind","group","title","body","created_at","read"]}}},"required":["notifications"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/notifications/device":{"post":{"operationId":"trainerNotification.registerDevice","summary":"POST /me/trainer/notifications/device \u2014 the coach app registers its FCM token for push.\nOwner is the trainer's Employee; a token is unique within a brand (re-points on re-register)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","maxLength":512},"platform":{"type":["string","null"],"enum":["android","ios","web"]},"app":{"type":["string","null"],"maxLength":20}},"required":["token"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/notifications/device/{token}":{"delete":{"operationId":"trainerNotification.unregisterDevice","summary":"DELETE /me/trainer/notifications/device/{token} \u2014 unregister a device (logout)","tags":["Member App (self-service)"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/notifications/read-all":{"post":{"operationId":"trainerNotification.readAll","summary":"POST /me/trainer/notifications/read-all","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/notifications/{notification}/read":{"post":{"operationId":"trainerNotification.readOne","summary":"POST /me/trainer/notifications/{notification}/read","tags":["Member App (self-service)"],"parameters":[{"name":"notification","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/profile":{"get":{"operationId":"trainerProfile.show","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"profile":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"anyOf":[{"type":"string"},{"type":"null"}]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"},"rating":{"type":["number","null"]},"reviews_count":{"type":"integer"},"bio":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"specialties":{"type":"array","items":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"languages":{"anyOf":[{"type":"array","items":{}},{"type":"array","prefixItems":[{"type":"string","const":"ar"},{"type":"string","const":"en"}],"minItems":2,"maxItems":2,"additionalItems":false}]},"certifications":{"anyOf":[{"type":"array","prefixItems":[{"type":"object","properties":{"name":{"type":["string","null"]},"status":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"detail":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"expiring_soon":{"type":"boolean"},"renewable":{"type":"boolean"}},"required":["name","status","detail","expiring_soon","renewable"]}],"minItems":1,"maxItems":1,"additionalItems":false},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"avatar_url":{"type":["string","null"]},"photo_policy_note":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name","initial","rating","reviews_count","bio","specialties","languages","certifications","avatar_url","photo_policy_note"]}},"required":["profile"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"trainerProfile.update","summary":"PATCH /me/trainer/profile \u2014 the coach's self-editable fields (bio, languages)","tags":["Member App (self-service)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"bio":{"type":["string","null"]},"languages":{"type":"array","items":{"type":"string","enum":["ar","en"]}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"profile":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"anyOf":[{"type":"string"},{"type":"null"}]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"},"rating":{"type":["number","null"]},"reviews_count":{"type":"integer"},"bio":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"specialties":{"type":"array","items":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"languages":{"anyOf":[{"type":"array","items":{}},{"type":"array","prefixItems":[{"type":"string","const":"ar"},{"type":"string","const":"en"}],"minItems":2,"maxItems":2,"additionalItems":false}]},"certifications":{"anyOf":[{"type":"array","prefixItems":[{"type":"object","properties":{"name":{"type":["string","null"]},"status":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"detail":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"expiring_soon":{"type":"boolean"},"renewable":{"type":"boolean"}},"required":["name","status","detail","expiring_soon","renewable"]}],"minItems":1,"maxItems":1,"additionalItems":false},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"avatar_url":{"type":["string","null"]},"photo_policy_note":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name","initial","rating","reviews_count","bio","specialties","languages","certifications","avatar_url","photo_policy_note"]}},"required":["profile"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/clients":{"get":{"operationId":"trainerClient.index","summary":"GET /me/trainer/clients","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"clients":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"initial":{"type":"string"},"streak":{"type":"integer"},"weekly_attendance":{"type":"object","properties":{"done":{"type":"integer","minimum":0},"target":{"type":"integer","const":5}},"required":["done","target"]},"weight_goal_pct":{"type":["integer","null"]},"days_since_visit":{"type":["integer","null"]},"program":{"type":["object","null"],"properties":{"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"week":{"type":["object","null"]},"total_weeks":{"type":"integer"}},"required":["name","week","total_weeks"]}},"required":["id","name","initial","streak","weekly_attendance","weight_goal_pct","days_since_visit","program"]}}},"required":["clients"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/clients/{member}":{"get":{"operationId":"trainerClient.show","summary":"GET /me/trainer/clients/{member} \u2014 the full progress profile","tags":["Member App (self-service)"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"client":{"type":"string"}},"required":["client"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/clients/{member}/adherence":{"get":{"operationId":"trainerClient.adherence","summary":"GET /me/trainer/clients/{member}/adherence \u2014 how the assigned program is being executed (C1)","tags":["Member App (self-service)"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"program_id":{"type":"string"},"weeks":{"type":"integer"},"current_week":{"type":"integer"},"adherence":{"type":"object","properties":{"completed_days":{"type":"integer","minimum":0},"scheduled_days":{"type":"string"},"pct":{"type":"integer"}},"required":["completed_days","scheduled_days","pct"]},"streak":{"type":"integer"},"last_workout_at":{"anyOf":[{"type":"string"},{"type":"null"}]},"days":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"last_completed_at":{"type":"string"},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"target":{"type":"object","properties":{"sets":{"type":"integer"},"reps":{"type":"integer"},"weight_kg":{"type":["number","null"]}},"required":["sets","reps","weight_kg"]},"actual":{"type":"object","properties":{"best_weight_kg":{"type":["number","null"]},"best_reps":{"type":["integer","null"]},"logged_sets":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]}},"required":["best_weight_kg","best_reps","logged_sets"]},"hit_target":{"type":"string"}},"required":["id","name","target","actual","hit_target"]}}},"required":["id","name","last_completed_at","exercises"]}}},"required":["program_id","weeks","current_week","adherence","streak","last_workout_at","days"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Not your client."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/clients/{member}/goals":{"post":{"operationId":"trainerClient.setGoal","summary":"POST /me/trainer/clients/{member}/goals \u2014 the coach sets a goal for the client (D3)","tags":["Member App (self-service)"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"unit":{"type":["string","null"]},"current":{"type":"number"},"target":{"type":"number"},"deadline":{"type":["string","null"],"format":"date-time"},"icon_key":{"type":["string","null"],"maxLength":40}},"required":["title"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"goal":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"unit":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"current":{"anyOf":[{"type":"integer"},{"type":"number"}]},"target":{"anyOf":[{"type":"integer"},{"type":"number"}]},"deadline":{"type":"string"},"set_by":{"type":"string"}},"required":["id","icon_key","title","unit","current","target","deadline","set_by"]}},"required":["goal"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/clients/{member}/goals/{goal}":{"patch":{"operationId":"trainerClient.updateGoal","summary":"PATCH /me/trainer/clients/{member}/goals/{goal} \u2014 the coach updates a client's goal progress","tags":["Member App (self-service)"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}},{"name":"goal","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"current":{"type":"number"},"target":{"type":"number"},"deadline":{"type":["string","null"],"format":"date-time"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"goal":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"title":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"unit":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"current":{"anyOf":[{"type":"integer"},{"type":"number"}]},"target":{"anyOf":[{"type":"integer"},{"type":"number"}]},"deadline":{"type":"string"},"set_by":{"type":"string"}},"required":["id","icon_key","title","unit","current","target","deadline","set_by"]}},"required":["goal"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"trainerClient.deleteGoal","summary":"DELETE /me/trainer/clients/{member}/goals/{goal} \u2014 the coach removes a client's goal","tags":["Member App (self-service)"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}},{"name":"goal","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Not your client."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/earnings":{"get":{"operationId":"trainerEarnings.index","summary":"GET /me/trainer/earnings?period=","tags":["Member App (self-service)"],"parameters":[{"name":"period","in":"query","schema":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}$"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}]},"total_minor":{"anyOf":[{"type":"integer"},{"type":"number"}]},"delta_pct":{"type":"integer"},"delta_vs":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}]},"lines":{"type":"array","items":{"type":"object","properties":{"kind":{"type":"string","enum":["pt","classes","bonus"]},"label":{"type":"string"},"count":{"type":"integer"},"unit":{"type":"string"},"rate_minor":{"type":"integer"},"amount_minor":{"anyOf":[{"type":"integer"},{"type":"number"}]},"note":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["kind","label","count","unit","rate_minor","amount_minor","note"]}}},"required":["period","total_minor","delta_pct","delta_vs","lines"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/earnings/statement":{"get":{"operationId":"trainerEarnings.statement","summary":"GET /me/trainer/earnings/statement?period= \u2014 a signed, printable statement URL","tags":["Member App (self-service)"],"parameters":[{"name":"period","in":"query","schema":{"type":["string","null"],"pattern":"^\\d{4}-\\d{2}$"}},{"name":"format","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/availability":{"get":{"operationId":"trainerAvailability.index","summary":"GET /me/trainer/availability","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"windows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"days_label":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"time_label":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}]},"state":{"type":"string","enum":["off","partial","available"]},"partial_note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","days_label","time_label","state","partial_note"]}},"time_off":{"type":["object","null"],"properties":{"hijri_label":{"type":["object","null"],"properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"gregorian_label":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"active":{"type":"boolean"},"conflict":{"type":"object","properties":{"note":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"pt_session_id":{"type":"string"},"client_name":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"time_label":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["note","pt_session_id","client_name","time_label"]}},"required":["hijri_label","gregorian_label","active","conflict"]}},"required":["windows","time_off"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/availability/windows/{window}":{"patch":{"operationId":"trainerAvailability.updateWindow","summary":"PATCH /me/trainer/availability/windows/{window} \u2014 `w0`..`w6`; state/windows","tags":["Member App (self-service)"],"parameters":[{"name":"window","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string","enum":["available","off"]},"windows":{"type":["array","null"],"items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}}}}},"required":["state"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"window":{"type":"object","properties":{"id":{"type":"string"},"days_label":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"time_label":{"anyOf":[{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}]},"state":{"type":"string","enum":["off","partial","available"]},"partial_note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","days_label","time_label","state","partial_note"]}},"required":["window"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/availability/time-off":{"put":{"operationId":"trainerAvailability.setTimeOff","summary":"PUT /me/trainer/availability/time-off {active, starts_on, ends_on}","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"},"starts_on":{"type":["string","null"],"format":"date-time"},"ends_on":{"type":["string","null"],"format":"date-time"}},"required":["active"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"time_off":{"type":["object","null"],"properties":{"hijri_label":{"type":["object","null"],"properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"gregorian_label":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"active":{"type":"boolean"},"conflict":{"type":"object","properties":{"note":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"pt_session_id":{"type":"string"},"client_name":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"time_label":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["note","pt_session_id","client_name","time_label"]}},"required":["hijri_label","gregorian_label","active","conflict"]}},"required":["time_off"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/exercise-library":{"get":{"operationId":"trainerExerciseLibrary.index","tags":["Member App (self-service)"],"parameters":[{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":120}},{"name":"muscle","in":"query","schema":{"type":["string","null"],"maxLength":60}},{"name":"equipment","in":"query","schema":{"type":["string","null"],"maxLength":60}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"exercises":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"muscle":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"equipment":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"thumbnail_url":{"type":["string","null"]},"default_sets":{"type":"string"},"default_reps":{"type":"string"},"default_rest_sec":{"type":"string"}},"required":["id","name","muscle","equipment","thumbnail_url","default_sets","default_reps","default_rest_sec"]},{"type":"string"}]}}},"required":["exercises"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/programs":{"get":{"operationId":"trainerProgram.index","summary":"GET /me/trainer/programs","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"programs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"assigned_to":{"type":["object","null"],"properties":{"client_id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["client_id","name"]},"weeks":{"type":"integer"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"is_active":{"type":"boolean"},"status":{"type":"string"},"assigned_at":{"type":"string"},"phases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]}},"required":["id","name","sets","reps","rest_sec","target_weight_kg","note","workout_item_id"]}}},"required":["id","name","weekday","exercises"]}}},"required":["id","assigned_to","weeks","goal","is_active","status","assigned_at","phases"]}}},"required":["programs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"trainerProgram.store","summary":"POST /me/trainer/programs \u2014 the builder's create (optionally with phases inline)","tags":["Member App (self-service)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assigned_to_client_id":{"type":["string","null"]},"name":{"type":["string","null"]},"weeks":{"type":"integer","minimum":1,"maximum":52},"goal":{"type":["string","null"]},"is_template":{"type":"boolean"},"is_public":{"type":"boolean","description":"Club-plan catalog fields (Phase 2) \u2014 only meaningful on a public template; all optional/additive."},"featured":{"type":"boolean"},"total_days":{"type":["integer","null"],"minimum":1,"maximum":366},"cover_url":{"type":["string","null"],"maxLength":1000},"level":{"type":["string","null"]},"catalog_meta":{"type":["array","null"],"items":{"type":"string"}},"phases":{"type":["array","null"],"items":{"type":"object","properties":{"name":{"type":"string"},"exercises":{"type":["array","null"],"items":{"type":"object","properties":{"exercise_id":{"type":"string"},"sets":{"type":"integer","minimum":1,"maximum":20},"reps":{"type":"integer","minimum":1,"maximum":100},"duration_sec":{"type":["integer","null"],"minimum":1,"maximum":3600},"rest_sec":{"type":"integer","minimum":0,"maximum":900},"target_weight_kg":{"type":["number","null"],"minimum":0,"maximum":1000},"note":{"type":["string","null"]}},"required":["exercise_id","sets","reps","rest_sec"]}},"weekday":{"type":["integer","null"],"minimum":0,"maximum":6}},"required":["name"]}}},"required":["weeks"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"program":{"type":"object","properties":{"id":{"type":"string"},"assigned_to":{"type":["object","null"],"properties":{"client_id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["client_id","name"]},"weeks":{"type":"integer"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"is_active":{"type":"boolean"},"status":{"type":"string"},"assigned_at":{"type":"string"},"phases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]}},"required":["id","name","sets","reps","rest_sec","target_weight_kg","note","workout_item_id"]}}},"required":["id","name","weekday","exercises"]}}},"required":["id","assigned_to","weeks","goal","is_active","status","assigned_at","phases"]}},"required":["program"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/program-templates":{"get":{"operationId":"trainerProgram.templates","summary":"GET /me/trainer/program-templates","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"weeks":{"type":"integer"},"meta":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name","weeks","meta"]}}},"required":["templates"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/programs/{program}":{"get":{"operationId":"trainerProgram.show","summary":"GET /me/trainer/programs/{program}","tags":["Member App (self-service)"],"parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"program":{"type":"object","properties":{"id":{"type":"string"},"assigned_to":{"type":["object","null"],"properties":{"client_id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["client_id","name"]},"weeks":{"type":"integer"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"is_active":{"type":"boolean"},"status":{"type":"string"},"assigned_at":{"type":"string"},"phases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]}},"required":["id","name","sets","reps","rest_sec","target_weight_kg","note","workout_item_id"]}}},"required":["id","name","weekday","exercises"]}}},"required":["id","assigned_to","weeks","goal","is_active","status","assigned_at","phases"]}},"required":["program"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/programs/{program}/phases":{"post":{"operationId":"trainerProgram.addPhase","summary":"POST /me/trainer/programs/{program}/phases {name}","tags":["Member App (self-service)"],"parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"weekday":{"type":["integer","null"],"minimum":0,"maximum":6}},"required":["name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"phase":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]}},"required":["id","name","sets","reps","rest_sec","target_weight_kg","note","workout_item_id"]}}},"required":["id","name","weekday","exercises"]}},"required":["phase"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/programs/{program}/phases/{phase}/exercises":{"post":{"operationId":"trainerProgram.addExercise","summary":"POST /me/trainer/programs/{program}/phases/{phase}/exercises","tags":["Member App (self-service)"],"parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string"}},{"name":"phase","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"exercise_id":{"type":"string"},"sets":{"type":"integer","minimum":1,"maximum":20},"reps":{"type":"integer","minimum":1,"maximum":100},"duration_sec":{"type":["integer","null"],"minimum":1,"maximum":3600},"rest_sec":{"type":"integer","minimum":0,"maximum":900},"target_weight_kg":{"type":["number","null"],"minimum":0,"maximum":1000},"note":{"type":["string","null"]}},"required":["exercise_id","sets","reps","rest_sec"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"phase":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]}},"required":["id","name","sets","reps","rest_sec","target_weight_kg","note","workout_item_id"]}}},"required":["id","name","weekday","exercises"]}},"required":["phase"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/programs/{program}/assign":{"post":{"operationId":"trainerProgram.assign","summary":"POST /me/trainer/programs/{program}/assign {client_id} \u2014 templates deep-clone","tags":["Member App (self-service)"],"parameters":[{"name":"program","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"}},"required":["client_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"program":{"type":"object","properties":{"id":{"type":"string"},"assigned_to":{"type":["object","null"],"properties":{"client_id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["client_id","name"]},"weeks":{"type":"integer"},"goal":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"is_active":{"type":"boolean"},"status":{"type":"string"},"assigned_at":{"type":"string"},"phases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"weekday":{"type":["integer","null"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets":{"type":"integer"},"reps":{"type":"integer"},"rest_sec":{"type":"integer"},"target_weight_kg":{"type":["number","null"]},"note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"workout_item_id":{"type":["string","null"]}},"required":["id","name","sets","reps","rest_sec","target_weight_kg","note","workout_item_id"]}}},"required":["id","name","weekday","exercises"]}}},"required":["id","assigned_to","weeks","goal","is_active","status","assigned_at","phases"]}},"required":["program"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/routine-reviews":{"get":{"operationId":"trainerRoutineReview.index","summary":"GET /me/trainer/routine-reviews","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"reviews":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name"]},"shared_label":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"status":{"type":"string"},"phase_name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets_reps":{"type":["string","null"]},"trainer_comment":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name","sets_reps","trainer_comment"]}},"general_note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","client","shared_label","status","phase_name","exercises","general_note"]}}},"required":["reviews"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/routine-reviews/{review}":{"get":{"operationId":"trainerRoutineReview.show","summary":"GET /me/trainer/routine-reviews/{review}","tags":["Member App (self-service)"],"parameters":[{"name":"review","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"review":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name"]},"shared_label":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"status":{"type":"string"},"phase_name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets_reps":{"type":["string","null"]},"trainer_comment":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name","sets_reps","trainer_comment"]}},"general_note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","client","shared_label","status","phase_name","exercises","general_note"]}},"required":["review"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/routine-reviews/{review}/approve":{"post":{"operationId":"trainerRoutineReview.approve","summary":"POST /me/trainer/routine-reviews/{review}/approve {note, exercise_comments[]}","tags":["Member App (self-service)"],"parameters":[{"name":"review","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":["string","null"]},"exercise_comments":{"type":["array","null"],"items":{"type":"object","properties":{"exercise_id":{"type":"string"},"comment":{"type":"string"}},"required":["exercise_id","comment"]}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"review":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name"]},"shared_label":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"status":{"type":"string"},"phase_name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets_reps":{"type":["string","null"]},"trainer_comment":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name","sets_reps","trainer_comment"]}},"general_note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","client","shared_label","status","phase_name","exercises","general_note"]}},"required":["review"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/routine-reviews/{review}/suggest-edit":{"post":{"operationId":"trainerRoutineReview.suggestEdit","summary":"POST /me/trainer/routine-reviews/{review}/suggest-edit {note} \u2014 stays pending","tags":["Member App (self-service)"],"parameters":[{"name":"review","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string"}},"required":["note"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"review":{"type":"object","properties":{"id":{"type":"string"},"client":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name"]},"shared_label":{"type":"object","properties":{"ar":{"type":"string"},"en":{"type":"string"}},"required":["ar","en"]},"status":{"type":"string"},"phase_name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"exercises":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"sets_reps":{"type":["string","null"]},"trainer_comment":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","name","sets_reps","trainer_comment"]}},"general_note":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]}},"required":["id","client","shared_label","status","phase_name","exercises","general_note"]}},"required":["review"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/me/trainer/chat-token":{"post":{"operationId":"trainerChatToken.store","tags":["Member App (self-service)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"url":{"type":"string"},"expires_in":{"type":"integer","const":3600}},"required":["token","url","expires_in"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/leads":{"post":{"operationId":"lead.store","summary":"Public web-form capture (brand-scoped, dedup against the Golden Record)","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"first_name_ar":{"type":["string","null"]},"last_name_ar":{"type":["string","null"]},"gender":{"$ref":"#/components/schemas/Gender"},"national_id":{"type":["string","null"]},"iqama_number":{"type":["string","null"]},"mobile":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email"},"nationality":{"type":["string","null"]},"lead_source":{"type":["string","null"]},"utm":{"type":["array","null"],"items":{"type":"string"}},"attribution":{"type":["array","null"],"items":{"type":"string"}},"consent_marketing":{"type":["boolean","null"]}},"required":["first_name","gender"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"is_existing":{"type":"boolean"},"pipeline_stage":{"type":"string"}},"required":["member_id","is_existing","pipeline_stage"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/leads/abandoned-cart":{"post":{"operationId":"lead.abandonedCart","summary":"Dropped-cart recovery (RFP 11-5.0): a public checkout that was abandoned mid-flow.\nCapture/dedup the Golden Record, open (or keep) a short-SLA prospect at the\n'abandoned_cart' source and log a follow-up note naming the plan/club so sales can\nchase the lead. Public + throttled + tenant-scoped, like /leads","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"mobile":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email"},"gender":{"$ref":"#/components/schemas/Gender"},"plan_id":{"type":["string","null"]},"club_id":{"type":["string","null"]},"attribution":{"type":["array","null"],"items":{"type":"string"}}},"required":["first_name","gender"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"pipeline_stage":{"type":"string"}},"required":["member_id","pipeline_stage"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/members/enroll":{"post":{"operationId":"enrollment.store","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"first_name_ar":{"type":["string","null"]},"last_name_ar":{"type":["string","null"]},"gender":{"$ref":"#/components/schemas/Gender"},"national_id":{"type":["string","null"]},"iqama_number":{"type":["string","null"]},"mobile":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email"},"nationality":{"type":["string","null"]},"dob":{"type":["string","null"],"format":"date-time"},"referral_source":{"$ref":"#/components/schemas/ReferralSource"},"emergency_contact_name":{"type":["string","null"]},"emergency_contact_phone":{"type":["string","null"]},"emergency_contact_relation":{"type":["string","null"]},"plan_id":{"type":"string"},"club_id":{"type":"string"},"channel":{"$ref":"#/components/schemas/SalesChannel"},"segment":{"$ref":"#/components/schemas/MemberSegment"},"payment_method":{"type":["string","null"],"description":"How the first charge is paid \u2014 a payment-method catalogue key (recorded on the payment).","maxLength":40},"coupon_code":{"type":["string","null"],"description":"Optional coupon \u2014 re-validated + redeemed server-side; invalid \u2192 no discount.","maxLength":60},"sold_by_employee_id":{"type":["string","null"],"description":"Credit the sale to a specific rep (RFP #1). Null \u2192 the acting staff user's own employee."},"assist_employee_id":{"type":["string","null"],"description":"An optional assisting rep who shares the commission per the brand's split policy."},"add_on_id":{"type":["string","null"]},"photo":{"type":["string","null"],"description":"Member photo as a base64 data URL (upload or live camera capture). Optional.\nPDPL: for female members it is destructively hashed and never persisted \u2014\nsee applyBiometric()."}},"required":["first_name","gender","plan_id","club_id","channel"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"is_existing":{"type":"boolean"},"contract_id":{"type":"string"},"payment_id":{"type":"string"},"amount_minor":{"type":"string"},"joining_fee_minor":{"type":"integer"},"currency":{"type":"string"},"addon_grant_id":{"type":"string"},"biometric":{"type":"object","properties":{"captured":{"type":"boolean"},"gender":{"anyOf":[{"type":"string"},{"type":"string","enum":["unknown"]}]},"hashed":{"type":"boolean"},"photo_stored":{"type":"boolean"},"photo_url":{"type":"null"}},"required":["captured","gender","hashed","photo_stored","photo_url"]}},"required":["member_id","is_existing","contract_id","payment_id","amount_minor","joining_fee_minor","currency","addon_grant_id","biometric"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/sellers":{"get":{"operationId":"enrollment.sellers","summary":"GET /members/sellers \u2014 active employees who can be credited as the seller of a sale (RFP #1),\nfor the \"Sold by\" picker on the enrol/POS form. Brand-scoped; gated by pos.sell with enrol","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sellers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"is_seller":{"type":"boolean","description":"Only a seller earns commission; the sale form warns when a non-seller is credited."}},"required":["id","name","name_ar","is_seller"]}}},"required":["sellers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/enroll-quote":{"get":{"operationId":"enrollment.quote","tags":["Members & CRM"],"parameters":[{"name":"plan_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"national_id","in":"query","schema":{"type":["string","null"]}},{"name":"iqama_number","in":"query","schema":{"type":["string","null"]}},{"name":"mobile","in":"query","schema":{"type":["string","null"]}},{"name":"segment","in":"query","schema":{"$ref":"#/components/schemas/MemberSegment"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"subscription_minor":{"type":"integer"},"joining_fee_minor":{"type":"integer"},"total_minor":{"type":"string"},"currency":{"type":"string","const":"SAR"},"is_renewal":{"type":"string","description":"True when this is a returning member or a renewal/reactivation sale \u2014 UI says \"no joining fee\"."}},"required":["subscription_minor","joining_fee_minor","total_minor","currency","is_renewal"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/duplicate-check":{"get":{"operationId":"enrollment.duplicates","summary":"Golden-Record pre-enrolment duplicate lookup (RFP 09 #4): given the identifiers the receptionist\nhas typed, return existing member records that match \u2014 so the UI can PROMPT to reuse or merge\nrather than silently create a second record. Brand-scoped (only this brand's members match)","tags":["Members & CRM"],"parameters":[{"name":"national_id","in":"query","schema":{"type":["string","null"]}},{"name":"iqama_number","in":"query","schema":{"type":["string","null"]}},{"name":"mobile","in":"query","schema":{"type":["string","null"]}},{"name":"email","in":"query","schema":{"type":["string","null"],"format":"email"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"matches":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"national_id":{"type":["string","null"]},"iqama_number":{"type":["string","null"]},"mobile":{"type":["string","null"]},"email":{"type":["string","null"]},"profile_type":{"type":"string"},"has_membership":{"type":"boolean"},"matched_on":{"type":"array","description":"Which identifier(s) this record matched on \u2014 drives the \"matched by National ID\" hint.","items":{}}},"required":["id","name","national_id","iqama_number","mobile","email","profile_type","has_membership","matched_on"]}}},"required":["matches"]},{"type":"object","properties":{"matches":{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}},"required":["matches"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/merge":{"post":{"operationId":"enrollment.merge","summary":"Golden-Record merge (RFP 09 #4): absorb a duplicate ({source_id}) into the surviving member\n({member}) \u2014 reassigns all member-scoped records + wallet balance, then removes the source","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"source_id":{"type":"string"}},"required":["source_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"name":{"type":"string"}},"required":["member_id","name"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/waitlist":{"get":{"operationId":"waitlist.index","summary":"GET /manage/waitlist?club_id= \u2014 the club's live waitlist (expires lapsed invites first)","tags":["Members & CRM"],"parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string","maxLength":40}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"position":{"type":"integer"},"status":{"type":"string"},"member_id":{"type":["string","null"]},"member_name":{"type":["string","null"]},"plan_id":{"type":["string","null"]},"plan_name":{"type":["string","null"]},"invited_at":{"type":"string"},"expires_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","position","status","member_id","member_name","plan_id","plan_name","invited_at","expires_at","created_at"]}}},"required":["entries"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/waitlist/{entry}/invite":{"post":{"operationId":"waitlist.invite","summary":"POST /manage/waitlist/{entry}/invite \u2014 open a 24h invite + notify the prospect","tags":["Members & CRM"],"parameters":[{"name":"entry","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"expires_at":{"type":"string"}},"required":["status","expires_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/waitlist/{entry}/convert":{"post":{"operationId":"waitlist.convert","summary":"POST /manage/waitlist/{entry}/convert \u2014 complete the sale (the member has paid) \u2192 a contract","tags":["Members & CRM"],"parameters":[{"name":"entry","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"contract_id":{"type":"string"}},"required":["status","contract_id"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"This waitlist entry has no member/plan to convert."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/waitlist/{entry}/cancel":{"post":{"operationId":"waitlist.cancel","summary":"POST /manage/waitlist/{entry}/cancel \u2014 drop a prospect from the list","tags":["Members & CRM"],"parameters":[{"name":"entry","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/health-waiver":{"post":{"operationId":"healthWaiver.update","summary":"POST /members/{member}/health-waiver { received? } \u2014 record (or clear) the member's PAR-Q waiver","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":["boolean","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"health_waiver_received":{"type":"boolean"},"health_waiver_at":{"type":"string"}},"required":["health_waiver_received","health_waiver_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"healthWaiver.removeDocument","summary":"DELETE /members/{member}/health-waiver \u2014 remove the uploaded PAR-Q file and reset the waiver status\n(the file was the evidence, so clearing it un-marks \"received\"). 404s if no file exists","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"removed":{"type":"boolean"},"health_waiver_received":{"type":"boolean"}},"required":["removed","health_waiver_received"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/health-waiver/remind":{"post":{"operationId":"healthWaiver.remind","summary":"POST /members/{member}/health-waiver/remind \u2014 nudge the member to complete the PAR-Q","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"reminded":{"type":"boolean"}},"required":["reminded"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/plan-summary":{"get":{"operationId":"membershipKpi.planSummary","summary":"Consolidated member plan management view (RFP 01 #13): the member's current\ncontract + plan, plus renewal eligibility. A member is renewal-eligible when their\nlatest non-terminal contract is within the active renew policy's grace window of\nits expiry (or already past expiry but inside grace). Contract is resolved\nbrand-scoped so a cross-brand member/contract is invisible","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"current_contract":{"type":["object","null"],"properties":{"id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"sale_date":{"type":"string"},"activation_date":{"type":"string"},"expiry_date":{"type":"string"},"auto_renew":{"type":"boolean"}},"required":["id","state","contract_type","amount_minor","currency","sale_date","activation_date","expiry_date","auto_renew"]},"plan":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"duration_months":{"type":["integer","null"]}},"required":["id","code","name","duration_months"]},"renewal":{"type":"object","properties":{"eligible":{"type":"boolean"},"grace_days":{"anyOf":[{"type":"string"},{"type":"integer","enum":[0]}]},"policy_id":{"type":"string"},"policy_name":{"type":"string"},"expires_at":{"type":["string","null"]}},"required":["eligible","grace_days","policy_id","policy_name","expires_at"]}},"required":["member_id","current_contract","plan","renewal"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/strikes":{"get":{"operationId":"strike.index","summary":"GET /members/{member}/strikes \u2014 staff audit of a member's strikes/waivers/bans","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"active_strikes":{"type":"integer","minimum":0},"threshold":{"type":"integer"},"window_days":{"type":"integer"},"active_ban":{"type":["object","null"],"properties":{"ends_at":{"type":"string"},"reason":{"type":"string"}},"required":["ends_at","reason"]},"strikes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string"},"created_at":{"type":"string","description":"no_show | late_cancel | non_confirmation"},"exempt":{"type":"boolean"},"exempt_reason":{"type":["string","null"]},"consumed":{"type":"boolean","description":"promoted_waitlist | illness | admin_waiver"},"waived":{"type":"boolean"},"waive_reason":{"type":["string","null"]},"waived_by":{},"counts":{"type":"boolean"}},"required":["id","reason","created_at","exempt","exempt_reason","consumed","waived","waive_reason","waived_by","counts"]}},"bans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"reason":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"strike_count":{"type":"integer"},"active":{"type":"string","description":"Active = still within its window AND not lifted early by a manager."},"lifted":{"type":"boolean"},"lifted_at":{"type":"string"},"lift_reason":{"type":["string","null"]},"lifted_by":{}},"required":["id","reason","starts_at","ends_at","strike_count","active","lifted","lifted_at","lift_reason","lifted_by"]}}},"required":["active_strikes","threshold","window_days","active_ban","strikes","bans"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/training-profile":{"get":{"operationId":"classWorkout.trainingProfile","summary":"GET /members/{member}/training-profile \u2014 the member's levels + 1RMs, plus the options to set them","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"class_types":{"type":"array","items":{"$ref":"#/components/schemas/EventConcept"}},"levels":{"type":"array","items":{"$ref":"#/components/schemas/WorkoutLevel"}},"member_levels":{"type":"array","items":{}},"exercises":{"type":"array","items":{"$ref":"#/components/schemas/FitnessExercise"}},"one_rms":{"type":"array","items":{"type":"object","properties":{"fitness_exercise_id":{"type":"string"},"name":{"type":"string"},"weight_kg":{"type":"number"}},"required":["fitness_exercise_id","name","weight_kg"]}}},"required":["class_types","levels","member_levels","exercises","one_rms"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/workout-level":{"post":{"operationId":"classWorkout.assignMemberLevel","summary":"POST /members/{member}/workout-level \u2014 assign a member's level for a class type","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_concept_id":{"type":"string"},"workout_level_id":{"type":"string"}},"required":["event_concept_id","workout_level_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/one-rep-maxes":{"put":{"operationId":"classWorkout.setOneRepMaxes","summary":"PUT /members/{member}/one-rep-maxes \u2014 upsert the member's 1RM (kg) per lift","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"fitness_exercise_id":{"type":"string"},"weight_kg":{"type":"number","minimum":0}},"required":["fitness_exercise_id","weight_kg"]},"minItems":1}},"required":["items"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/emergency-contact":{"patch":{"operationId":"memberProfile.updateEmergencyContact","summary":"PATCH /members/{member}/emergency-contact \u2014 additive, partial update","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"emergency_contact_name":{"type":["string","null"]},"emergency_contact_phone":{"type":["string","null"]},"emergency_contact_relation":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"emergency_contact_name":{"type":"string"},"emergency_contact_phone":{"type":"string"},"emergency_contact_relation":{"type":"string"}},"required":["id","emergency_contact_name","emergency_contact_phone","emergency_contact_relation"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/profile-details":{"patch":{"operationId":"memberProfile.updateProfileDetails","summary":"PATCH /members/{member}/profile-details \u2014 staff edit of the home branch + acquisition source","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"home_club_id":{"type":["string","null"]},"referral_source":{"$ref":"#/components/schemas/ReferralSource"},"account_owner_employee_id":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"home_club_id":{"type":"string"},"home_club_name":{},"referral_source":{"type":"string"},"account_owner_employee_id":{"type":"string"},"account_owner_name":{"type":["string","null"]}},"required":["id","home_club_id","home_club_name","referral_source","account_owner_employee_id","account_owner_name"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/contact":{"patch":{"operationId":"memberProfile.updateContact","description":"A duplicate email is reported as a WARNING, never a validation failure: households legitimately\nshare one inbox, so blocking would stop real corrections. The response lists the other members\nin this gym already using the address so the console can flag it. Member is brand-scoped, so\nthe duplicate lookup is automatically confined to the acting gym.","summary":"PATCH /members/{member}/contact \u2014 staff correction of a member's contact details (email)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":["string","null"],"format":"email","maxLength":190}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"duplicate_of":{"type":"array","items":{"type":"string"}}},"required":["id","email","duplicate_of"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/signatures":{"get":{"operationId":"signature.index","summary":"List a member's signature records, newest first, hash only","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"signatures":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"contract_id":{"type":["string","null"]},"document":{"type":"string"},"signature_hash":{"type":"string"},"signer_name":{"type":["string","null"]},"signed_at":{"type":"string"}},"required":["id","contract_id","document","signature_hash","signer_name","signed_at"]}}},"required":["signatures"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/bookings":{"get":{"operationId":"memberBookings.index","summary":"GET /members/{member}/bookings \u2014 the member's class/PT bookings + PT credit ledger","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"concept_name":{"type":"string","description":"pt | gx"},"club_name":{"type":["string","null"]},"starts_at":{"type":"string"},"trainer":{"type":["string","null"]},"booked_at":{"type":"string","description":"When the reservation was made + who made it (member self-service / staff / system)."},"booked_by":{"type":["string","null"]},"booked_by_type":{"type":["string","null"]},"status":{"type":"string"},"waitlist_position":{"type":["integer","null"]}},"required":["id","type","concept_name","club_name","starts_at","trainer","booked_at","booked_by","booked_by_type","status","waitlist_position"]}},"pt_credits":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"total":{"type":["integer","null"]},"used":{"type":"integer"},"remaining":{"type":["integer","null"]},"expires_at":{"type":"string"},"status":{"type":"string"}},"required":["name","total","used","remaining","expires_at","status"]}}},"required":["bookings","pt_credits"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/fitness":{"get":{"operationId":"memberFitness.show","summary":"GET /members/{member}/fitness","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"object","properties":{"workouts_this_week":{"type":"integer","minimum":0},"total_minutes":{"type":"integer"},"total_volume_kg":{"type":"integer"},"calories":{"type":"integer"},"active_streak":{"type":"integer","const":1}},"required":["workouts_this_week","total_minutes","total_volume_kg","calories","active_streak"]},"personal_records":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"icon_key":{"type":["string","null"]},"exercise":{"type":"object","properties":{"ar":{"type":["string","null"]},"en":{"type":"string"}},"required":["ar","en"]},"value":{"anyOf":[{"type":"integer"},{"type":"number"}]},"unit":{"type":["object","null"],"properties":{"ar":{"type":["string","null"]},"en":{"type":["string","null"]}},"required":["ar","en"]},"delta":{"anyOf":[{"type":"integer"},{"type":"number"}]},"achieved_at":{"type":"string"}},"required":["id","icon_key","exercise","value","unit","delta","achieved_at"]}},"log":{"type":"array","items":{}},"wod_scores":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"title_ar":{"type":"string"},"date":{"type":"string"},"scale":{"type":["string","null"]},"result":{"type":"string"}},"required":["title","title_ar","date","scale","result"]}},"benchmark_prs":{"type":"array","items":{}}},"required":["summary","personal_records","log","wod_scores","benchmark_prs"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/tasks":{"get":{"operationId":"memberTask.index","summary":"GET /members/{member}/tasks \u2014 the member's tasks, open first then by due date","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"type":{"type":"string"},"reason":{"type":"string"},"due_at":{"type":"string"},"status":{"type":"string"},"owner_employee_id":{"type":["string","null"]},"owner_name":{"type":["string","null"]},"resolved_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","member_id","type","reason","due_at","status","owner_employee_id","owner_name","resolved_at","created_at"]}}},"required":["tasks"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"memberTask.store","summary":"POST /members/{member}/tasks \u2014 open a follow-up task for the member","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":500},"due_at":{"type":["string","null"],"format":"date-time"},"owner_employee_id":{"type":["string","null"]}},"required":["reason"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"type":{"type":"string"},"reason":{"type":"string"},"due_at":{"type":"string"},"status":{"type":"string"},"owner_employee_id":{"type":["string","null"]},"owner_name":{"type":["string","null"]},"resolved_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","member_id","type","reason","due_at","status","owner_employee_id","owner_name","resolved_at","created_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/tasks/{task}":{"patch":{"operationId":"memberTask.update","summary":"PATCH /members/{member}/tasks/{task} {status} \u2014 mark a task done or re-open it","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}},{"name":"task","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","done"]}},"required":["status"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"type":{"type":"string"},"reason":{"type":"string"},"due_at":{"type":"string"},"status":{"type":"string"},"owner_employee_id":{"type":["string","null"]},"owner_name":{"type":["string","null"]},"resolved_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","member_id","type","reason","due_at","status","owner_employee_id","owner_name","resolved_at","created_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/loyalty":{"get":{"operationId":"memberEngagement.loyalty","summary":"GET /members/{member}/loyalty \u2014 points balance, tier, next-tier progress + recent ledger","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"enabled":{"type":"boolean"},"points_balance":{"type":"integer"},"lifetime_points":{"type":"integer"},"tier":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"cashback_percent":{"type":"integer"},"points_multiplier":{"type":"number"}},"required":["id","name","name_ar","cashback_percent","points_multiplier"]},"next_tier":{"type":["object","null"],"properties":{"name":{"type":"string"},"name_ar":{"type":["string","null"]},"min_lifetime_points":{"type":"integer"},"points_to_go":{"type":["object","null"]}},"required":["name","name_ar","min_lifetime_points","points_to_go"]},"ledger":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"delta":{"type":"integer"},"reason":{"type":"string"},"balance_after":{"type":"integer"},"created_at":{"type":"string"}},"required":["id","delta","reason","balance_after","created_at"]}}},"required":["enabled","points_balance","lifetime_points","tier","next_tier","ledger"]},{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/referrals":{"get":{"operationId":"memberEngagement.referrals","summary":"GET /members/{member}/referrals \u2014 the member's invite code (minted if missing) + who they referred","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"reward_per_friend_minor":{"type":"integer"},"referrals":{"type":"array","items":{"type":"object","properties":{"name":{"type":["string","null"]},"status":{"type":"string"},"reward_minor":{"type":"integer"},"created_at":{"type":"string"}},"required":["name","status","reward_minor","created_at"]}},"invited":{"type":"integer","minimum":0},"rewarded":{"type":"integer","minimum":0}},"required":["code","reward_per_friend_minor","referrals","invited","rewarded"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/visits":{"get":{"operationId":"memberEngagement.visits","summary":"GET /members/{member}/visits \u2014 recent gate check-ins (AccessEvent) + last-visit timestamp","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"last_check_in_at":{"type":"string"},"total":{"type":"integer","minimum":0},"visits":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"occurred_at":{"type":"string"},"club_id":{"type":"string"},"club_name":{},"outcome":{"type":"string","description":"Raw values so a legacy enum value can't 500 the panel."},"reason":{"type":"string"}},"required":["id","occurred_at","club_id","club_name","outcome","reason"]}}},"required":["last_check_in_at","total","visits"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/social":{"get":{"operationId":"socialAdmin.memberSocial","summary":"GET /members/{member}/social \u2014 a member's social footprint for the Member-360 panel","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"muted":{"type":"boolean"},"code":{"type":"string"},"friends_count":{"type":"integer"},"friends":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}},"blocked":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}},"pending_requests":{"type":"integer","minimum":0},"challenges":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"metric":{"type":"string"},"status":{"type":"string"},"duration_days":{"type":"integer"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"creator":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"participants":{"type":"array","items":{"type":"object","properties":{"member":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]},{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string","const":"Member"},"avatar_url":{"type":"null"},"is_me":{"type":"boolean"}},"required":["id","name","avatar_url","is_me"]}]},"score":{"type":"integer"},"accepted":{"type":"boolean"}},"required":["member","score","accepted"]}}},"required":["id","metric","status","duration_days","starts_at","ends_at","creator","participants"]}}},"required":["enabled","muted","code","friends_count","friends","blocked","pending_requests","challenges"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/social/reset-code":{"post":{"operationId":"socialAdmin.resetCode","summary":"POST /members/{member}/social/reset-code \u2014 re-mint the member's friend code","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/social/friends/{friend}":{"delete":{"operationId":"socialAdmin.removeFriendship","summary":"DELETE /members/{member}/social/friends/{friend} \u2014 sever a friendship (both sides)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}},{"name":"friend","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/social/mute":{"post":{"operationId":"socialAdmin.toggleMute","summary":"POST /members/{member}/social/mute \u2014 freeze / unfreeze a member's social access","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"muted":{"type":"boolean"}},"required":["muted"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"muted":{"type":"boolean"}},"required":["muted"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/documents":{"get":{"operationId":"memberDocument.index","summary":"GET /members/{member}/documents \u2014 a member's documents with their validation status","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"documents":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"original_name":{"type":"string"},"mime":{"type":["string","null"]},"size":{"type":"integer"},"status":{"type":"string"},"expiry_date":{"type":"string"},"uploaded_at":{"type":"string"}},"required":["id","type","original_name","mime","size","status","expiry_date","uploaded_at"]}}},"required":["documents"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"memberDocument.store","summary":"POST /members/{member}/documents \u2014 staff upload a document to the member's vault (auto-approved)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":[""]},"file":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","maxLength":8192},"expiry_date":{"type":["string","null"],"format":"date-time"}},"required":["type","file"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/documents/{document}/download":{"get":{"operationId":"memberDocument.download","summary":"GET /members/{member}/documents/{document}/download \u2014 stream a member's document for review","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}},{"name":"document","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"text/html":{"schema":{"type":"string"}}},"headers":{"Transfer-Encoding":{"required":true,"schema":{"type":"string","enum":["chunked"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/blacklist":{"get":{"operationId":"blacklist.index","summary":"GET /manage/blacklist \u2014 the brand's entries (newest first), optionally ?status=active","tags":["Members & CRM"],"parameters":[{"name":"status","in":"query","schema":{"type":["string","null"],"enum":["pending","active","removed"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"entries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"scope":{"type":"string"},"member_id":{"type":["string","null"]},"name":{"type":["string","null"]},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"national_id":{"type":["string","null"]},"reason":{"type":["string","null"]},"case_id":{"type":["string","null"]},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","scope","member_id","name","email","mobile","national_id","reason","case_id","status","created_at"]}}},"required":["entries"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"blacklist.store","summary":"POST /manage/blacklist \u2014 raise an add-to-blacklist request (pending until approved)","tags":["Members & CRM"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":["string","null"],"enum":["member","trainer","person"]},"member_id":{"type":["string","null"],"maxLength":40},"name":{"type":["string","null"],"maxLength":160},"email":{"type":["string","null"],"format":"email","maxLength":160},"mobile":{"type":["string","null"],"maxLength":20},"national_id":{"type":["string","null"],"maxLength":20},"biometric_ref":{"type":["string","null"],"maxLength":120},"reason":{"type":["string","null"],"maxLength":500},"case_id":{"type":["string","null"],"maxLength":40}}}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"entry":{"type":"object","properties":{"id":{"type":"string"},"scope":{"type":"string"},"member_id":{"type":["string","null"]},"name":{"type":["string","null"]},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"national_id":{"type":["string","null"]},"reason":{"type":["string","null"]},"case_id":{"type":["string","null"]},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","scope","member_id","name","email","mobile","national_id","reason","case_id","status","created_at"]},"approval_id":{"type":"string"}},"required":["entry","approval_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/blacklist/external-pt-violation":{"post":{"operationId":"blacklist.externalPtViolation","summary":"POST /manage/blacklist/external-pt-violation (RFP 05 #17) \u2014 raise the combined case: on dual\napproval, blacklist the member AND the unauthorised trainer (linked) AND cancel the member's\ncontract with no refund","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string","maxLength":40},"contract_id":{"type":["string","null"],"maxLength":40},"trainer_name":{"type":"string","maxLength":160},"trainer_email":{"type":["string","null"],"format":"email","maxLength":160},"trainer_mobile":{"type":["string","null"],"maxLength":20},"trainer_national_id":{"type":["string","null"],"maxLength":20},"reason":{"type":["string","null"],"maxLength":500}},"required":["member_id","trainer_name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"case_id":{"type":["string","null"]},"member_entry":{"type":"object","properties":{"id":{"type":"string"},"scope":{"type":"string"},"member_id":{"type":["string","null"]},"name":{"type":["string","null"]},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"national_id":{"type":["string","null"]},"reason":{"type":["string","null"]},"case_id":{"type":["string","null"]},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","scope","member_id","name","email","mobile","national_id","reason","case_id","status","created_at"]},"trainer_entry":{"type":"object","properties":{"id":{"type":"string"},"scope":{"type":"string"},"member_id":{"type":["string","null"]},"name":{"type":["string","null"]},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"national_id":{"type":["string","null"]},"reason":{"type":["string","null"]},"case_id":{"type":["string","null"]},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","scope","member_id","name","email","mobile","national_id","reason","case_id","status","created_at"]},"approval_id":{"type":"string"}},"required":["case_id","member_entry","trainer_entry","approval_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/blacklist/{entry}":{"delete":{"operationId":"blacklist.destroy","summary":"DELETE /manage/blacklist/{entry} \u2014 raise a remove-from-blacklist request (pending until approved)","tags":["Members & CRM"],"parameters":[{"name":"entry","in":"path","required":true,"schema":{"type":"string"}},{"name":"reason","in":"query","schema":{"type":["string","null"],"maxLength":500}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"approval_id":{"type":"string"}},"required":["approval_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/addons":{"post":{"operationId":"addOn.grant","summary":"Grant an add-on to a member","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"add_on_id":{"type":"string"}},"required":["add_on_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"add_on_id":{"type":"string"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]},"credits_total":{"type":["integer","null"]},"credits_used":{"type":"integer"},"remaining_credits":{"type":["integer","null"]},"expires_at":{"type":"string"},"status":{"type":"string"}},"required":["id","member_id","add_on_id","add_on","credits_total","credits_used","remaining_credits","expires_at","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"get":{"operationId":"addOn.memberGrants","summary":"List a member's add-on grants with remaining credits","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"grants":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"add_on_id":{"type":"string"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]},"credits_total":{"type":["integer","null"]},"credits_used":{"type":"integer"},"remaining_credits":{"type":["integer","null"]},"expires_at":{"type":"string"},"status":{"type":"string"}},"required":["id","member_id","add_on_id","add_on","credits_total","credits_used","remaining_credits","expires_at","status"]}}},"required":["grants"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/alerts":{"get":{"operationId":"memberAlert.index","summary":"RFP-12 #17: prioritised, colour-coded alerts for a member, computed live.\nGated by members.view in routes/api.php","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"alerts":{"type":"array","items":{}}},"required":["member_id","alerts"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/addon-bundles":{"post":{"operationId":"addOnBundle.grant","summary":"Grant a bundle (all of its component add-ons) to a member","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"bundle_id":{"type":"string"}},"required":["bundle_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"bundle_id":{"type":"string"},"member_id":{"type":"string"},"grant_ids":{"type":"array","items":{"type":"string"}}},"required":["bundle_id","member_id","grant_ids"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/free-trial-rules":{"get":{"operationId":"freeTrialRule.index","summary":"List the brand's free add-on trigger rules (active and inactive)","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"free_trial_rules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":"string"},"add_on_id":{"type":"string"},"is_active":{"type":"boolean"},"membership_plan":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]}},"required":["id","membership_plan_id","add_on_id","is_active","membership_plan","add_on"]}}},"required":["free_trial_rules"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"freeTrialRule.store","summary":"Create a free add-on trigger rule","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"membership_plan_id":{"type":"string"},"add_on_id":{"type":"string"},"is_active":{"type":"boolean"}},"required":["membership_plan_id","add_on_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":"string"},"add_on_id":{"type":"string"},"is_active":{"type":"boolean"},"membership_plan":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]}},"required":["id","membership_plan_id","add_on_id","is_active","membership_plan","add_on"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/free-trial-rules/{rule}":{"patch":{"operationId":"freeTrialRule.update","summary":"Update a free add-on trigger rule (e.g. toggle is_active or re-point it)","tags":["Members & CRM"],"parameters":[{"name":"rule","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"membership_plan_id":{"type":"string"},"add_on_id":{"type":"string"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":"string"},"add_on_id":{"type":"string"},"is_active":{"type":"boolean"},"membership_plan":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]}},"required":["id","membership_plan_id","add_on_id","is_active","membership_plan","add_on"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/automation-history":{"get":{"operationId":"memberAutomationHistory.index","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"runs":{"type":"array","items":{"type":"object","properties":{"rule_name":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"trigger":{"type":["string","null"]},"action":{"type":["string","null"]},"ran_at":{"type":"string"},"converted_at":{"type":"string"},"revenue_minor":{"type":"integer"}},"required":["rule_name","trigger","action","ran_at","converted_at","revenue_minor"]}},"enrollments":{"type":"array","items":{"type":"object","properties":{"sequence_name":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"trigger":{"type":["string","null"]},"enrolled_at":{"type":"string"},"status":{"type":"string"},"current_step":{"type":"integer"},"total_steps":{"type":"integer"},"last_fired_at":{"type":"string"},"converted_at":{"type":"string"},"revenue_minor":{"type":"integer"}},"required":["sequence_name","trigger","enrolled_at","status","current_step","total_steps","last_fired_at","converted_at","revenue_minor"]}}},"required":["runs","enrollments"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/communication-preferences":{"get":{"operationId":"memberPreference.index","summary":"GET /members/{member}/communication-preferences (members.view)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string"},"category":{"type":"string"},"opted_in":{"type":"boolean"}},"required":["channel","category","opted_in"]}}},"required":["preferences"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"memberPreference.update","summary":"PUT /members/{member}/communication-preferences (members.manage) \u2014 upsert","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string","enum":["sms","email","push","whatsapp"]},"category":{"type":"string","enum":["marketing","billing","operational","scheduling"]},"opted_in":{"type":"boolean"}},"required":["channel","category","opted_in"]},"minItems":1}},"required":["preferences"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"preferences":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string"},"category":{"type":"string"},"opted_in":{"type":"boolean"}},"required":["channel","category","opted_in"]}}},"required":["preferences"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/change-log":{"get":{"operationId":"memberPreference.changeLog","summary":"GET /members/{member}/change-log (members.pii.view) \u2014 newest first","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"change_log":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"old_value":{"type":["string","null"]},"new_value":{"type":["string","null"]},"changed_at":{"type":"string"}},"required":["field","old_value","new_value","changed_at"]}}},"required":["change_log"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/activities":{"get":{"operationId":"memberActivity.index","summary":"List the current brand's activity / complaint ledger for a member (newest first)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"activities":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"type":{"type":"string"},"subject":{"type":"string"},"body":{"type":["string","null"]},"status":{"type":"string"},"channel":{"type":["string","null"]},"direction":{"type":["string","null"]},"outcome":{"type":["string","null"]},"duration_sec":{"type":["integer","null"]},"sla_due_at":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"},"image_url":{"type":["string","null"]}},"required":["id","member_id","type","subject","body","status","channel","direction","outcome","duration_sec","sla_due_at","resolved_at","created_at","image_url"]}}},"required":["activities"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"memberActivity.store","summary":"Log a new activity / complaint against a member in the current brand","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/ActivityType"},"subject":{"type":"string","maxLength":255},"body":{"type":["string","null"]},"sla_due_at":{"type":["string","null"],"format":"date-time"},"image":{"type":["string","null"],"description":"Optional complaint photo as a data-URL (#8) \u2014 capped ~4MB of base64.","maxLength":5600000}},"required":["type","subject"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"type":{"type":"string"},"subject":{"type":"string"},"body":{"type":["string","null"]},"status":{"type":"string"},"channel":{"type":["string","null"]},"direction":{"type":["string","null"]},"outcome":{"type":["string","null"]},"duration_sec":{"type":["integer","null"]},"sla_due_at":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"},"image_url":{"type":["string","null"]}},"required":["id","member_id","type","subject","body","status","channel","direction","outcome","duration_sec","sla_due_at","resolved_at","created_at","image_url"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/support-tickets":{"get":{"operationId":"supportTicketAdmin.forMember","summary":"A single member's support tickets, for the Member-360 page. Gated by members.view (not the\nconfig.manage support desk), so any staff who can view the member sees their tickets + threads.\nBrand-scoped via the SupportTicket global scope","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"member_name":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","member_name","subject","category","status","created_at","updated_at","messages"]}},"open_count":{"type":"integer","minimum":0}},"required":["tickets","open_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/timeline":{"get":{"operationId":"memberActivity.timeline","summary":"Unified CRM timeline for a member (RFP Module 11): the activity / audit ledger\n(lead captured, stage changes, assignment, free visit, conversion, complaints\u2026)\nmerged with the free-form CRM notes, newest first. Each item is normalised to\n{ id, type, title, body, actor, at } so the front-end renders one stream","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"timeline":{"type":"array","items":{"type":"string"}}},"required":["timeline"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/membership-history":{"get":{"operationId":"memberActivity.membershipHistory","summary":"GET /members/{member}/membership-history \u2014 the member's dedicated membership lifecycle log\n(created / activated / frozen / cancelled / \u2026) with the acting user, newest first. Read-only;\nwritten automatically by RecordMembershipActivity on the contract domain events","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"history":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"body":{"type":["string","null"]},"actor":{"type":["string","null"]},"at":{"type":"string"}},"required":["id","type","title","body","actor","at"]}}},"required":["history"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/freezes":{"get":{"operationId":"freeze.history","summary":"GET /members/{member}/freezes \u2014 staff-visible freeze history for a member (every freeze across\ntheir contracts, newest first) so reception can see the details of past/current freezes","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"freezes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"starts_on":{"type":"string"},"ends_on":{"type":"string"},"requested_days":{"type":"integer"},"status":{"type":"string"},"is_paid":{"type":"boolean"},"fee_minor":{"type":"integer"},"currency":{"type":"string"},"back_dated":{"type":"boolean"},"reason_code":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","starts_on","ends_on","requested_days","status","is_paid","fee_minor","currency","back_dated","reason_code","created_at"]}}},"required":["freezes"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/contacts":{"post":{"operationId":"memberActivity.logContact","summary":"Log a contact attempt (RFP 11) \u2014 a call/message/visit with an outcome, on a lead OR a member\n(e.g. renewal outreach). Stored as a Contact activity so it joins the unified timeline; a\nnext-follow-up date keeps it open (feeding the follow-up list), otherwise it's resolved on log","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string"},"direction":{"type":["string","null"]},"outcome":{"type":"string"},"body":{"type":["string","null"]},"next_follow_up_at":{"type":["string","null"],"format":"date-time"},"duration_sec":{"type":["integer","null"],"description":"Call talk time in seconds (Phase 3 call tracking) \u2014 optional, only meaningful for calls.","minimum":0,"maximum":86400},"owner_employee_id":{"type":["string","null"],"description":"Optional: (re)assign the rep who owns this member/lead as part of logging the contact."}},"required":["channel","outcome"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"type":{"type":"string"},"subject":{"type":"string"},"body":{"type":["string","null"]},"status":{"type":"string"},"channel":{"type":["string","null"]},"direction":{"type":["string","null"]},"outcome":{"type":["string","null"]},"duration_sec":{"type":["integer","null"]},"sla_due_at":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"},"image_url":{"type":["string","null"]}},"required":["id","member_id","type","subject","body","status","channel","direction","outcome","duration_sec","sla_due_at","resolved_at","created_at","image_url"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/retention/overview":{"get":{"operationId":"retention.overview","description":"Engagement is the LATEST of a gate check-in, a member-app login or a class booking;\nrisk thresholds come from config/retention.php. The at_risk list is filterable\n(risk, never_checked_in, name, mobile) and paginated (page/per_page + meta). The\nKPI counts stay global (cached per brand) \u2014 filtering the table never changes the\nheadline numbers.","summary":"GET /retention/overview \u2014 at-risk Active members plus headline counts","tags":["Members & CRM"],"parameters":[{"name":"risk","in":"query","schema":{"type":["string","null"],"enum":["high","medium"]}},{"name":"never_checked_in","in":"query","schema":{"type":["boolean","null"]}},{"name":"name","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"mobile","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"],"minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"counts":{"type":"object","properties":{"active":{"type":"integer"},"at_risk":{"type":"integer"},"high":{"type":"integer"},"medium":{"type":"integer"},"never_checked_in":{"type":"integer"}},"required":["active","at_risk","high","medium","never_checked_in"]},"dormancy_days":{"type":"integer"},"at_risk":{"type":"array","items":{"type":"object","properties":{"member_id":{"type":"string"},"name":{"type":"string"},"mobile":{"type":"string"},"last_check_in_at":{"type":"string","description":"Legacy fields (back-compat) \u2014 prefer last_activity_at / days_since_activity."},"days_since_check_in":{"type":["integer","null"]},"never_checked_in":{"type":"boolean"},"last_activity_at":{"type":"string"},"days_since_activity":{"type":"integer"},"last_login_at":{"type":"string"},"last_booked_at":{"type":"string"},"risk":{"type":["string","null"]},"owner_employee_id":{"type":["string","null"]},"owner_name":{"type":["string","null"]},"open_tasks":{"type":"integer"}},"required":["member_id","name","mobile","last_check_in_at","days_since_check_in","never_checked_in","last_activity_at","days_since_activity","last_login_at","last_booked_at","risk","owner_employee_id","owner_name","open_tasks"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"string"},"last_page":{"type":["object","null"]}},"required":["page","per_page","total","last_page"]}},"required":["counts","dormancy_days","at_risk","meta"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/retention/command":{"get":{"operationId":"retention.command","summary":"GET /retention/command \u2014 the Retention Command Center (Phase 2). Churn-band distribution, the\nmoney number (revenue-at-risk = active-contract value of at-risk + critical members), and a\nprioritised \"save list\" ordered by risk \u00d7 value, each row carrying the churn score, band, the\nreasons, the recommended save action, and the member's contract value. Reuses the nightly snapshot,\nrefreshing it (base + churn) when the Active-member set has drifted or was never scored","tags":["Members & CRM"],"parameters":[{"name":"band","in":"query","schema":{"type":["string","null"],"enum":["all","critical","at_risk","watch"]}},{"name":"name","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"],"minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"distribution":{"type":"object","properties":{"healthy":{"type":"integer"},"watch":{"type":"integer"},"at_risk":{"type":"integer"},"critical":{"type":"integer"},"scored":{"anyOf":[{"type":"integer"},{"type":"number"}]}},"required":["healthy","watch","at_risk","critical","scored"]},"revenue_at_risk_minor":{"type":"integer"},"save_list":{"type":"array","items":{"type":"object","properties":{"member_id":{"type":"string"},"name":{"type":"string"},"mobile":{"type":"string"},"churn_score":{"type":"integer"},"churn_band":{"type":["string","null"]},"reasons":{"type":"array","items":{}},"recommended_action":{"type":["string","null"]},"value_minor":{"type":"integer"},"last_activity_at":{"type":"string"},"days_since_activity":{"type":"integer"},"never_checked_in":{"type":"boolean"},"owner_employee_id":{"type":["string","null"]},"owner_name":{"type":["string","null"]},"open_tasks":{"type":"integer"}},"required":["member_id","name","mobile","churn_score","churn_band","reasons","recommended_action","value_minor","last_activity_at","days_since_activity","never_checked_in","owner_employee_id","owner_name","open_tasks"]}},"dormancy_days":{"type":"integer"},"currency":{"type":"string","const":"SAR"},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"string"},"last_page":{"type":["object","null"]}},"required":["page","per_page","total","last_page"]}},"required":["distribution","revenue_at_risk_minor","save_list","dormancy_days","currency","meta"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/retention/scan":{"post":{"operationId":"retention.scan","summary":"Demo trigger for capability 5: run the dormancy scan for the current brand only and\nreport how many tasks were opened (also refreshes the engagement snapshot).\nIdempotent \u2014 re-running will not duplicate open dormancy tasks. Gated by\nretention.view in routes/api.php","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"}},"required":["created"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tasks":{"get":{"operationId":"retention.tasks","summary":"GET /tasks \u2014 the worklist: open (or done) tasks for the current brand, filterable by type,\ndue-bucket (overdue|today|upcoming|none) and member name, paginated. Ordered most-urgent first\n(dated tasks by due date, undated last)","tags":["Members & CRM"],"parameters":[{"name":"type","in":"query","schema":{"type":"string","maxLength":64}},{"name":"status","in":"query","schema":{"type":"string","enum":["open","done"]}},{"name":"member_id","in":"query","schema":{"type":"string"}},{"name":"due","in":"query","schema":{"type":"string","enum":["overdue","today","upcoming","none"]}},{"name":"q","in":"query","schema":{"type":"string","maxLength":100}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"member_name":{"type":["string","null"]},"owner_employee_id":{"type":["string","null"]},"type":{"type":"string"},"reason":{"type":"string"},"due_at":{"type":"string"},"status":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","member_id","member_name","owner_employee_id","type","reason","due_at","status","resolved_at","created_at"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"string"},"last_page":{"type":["object","null"]}},"required":["page","per_page","total","last_page"]}},"required":["tasks","meta"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"retention.store","summary":"POST /tasks \u2014 open a manual follow-up task for a member (retention row action).\nThe member must belong to this brand (contract or prospect profile) \u2014 same\ncross-brand IDOR guard the rest of the member surface uses","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"reason":{"type":"string","maxLength":500},"type":{"type":["string","null"],"maxLength":64},"due_at":{"type":["string","null"],"format":"date-time"},"owner_employee_id":{"type":["string","null"]}},"required":["member_id","reason"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"member_name":{"type":["string","null"]},"owner_employee_id":{"type":["string","null"]},"type":{"type":"string"},"reason":{"type":"string"},"due_at":{"type":"string"},"status":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","member_id","member_name","owner_employee_id","type","reason","due_at","status","resolved_at","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tasks/bulk":{"post":{"operationId":"retention.bulkStore","summary":"POST /tasks/bulk \u2014 open the same follow-up task for many members at once (bulk\nrow action on the at-risk table). Members outside the brand are skipped and\nreported, never created","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":500},"due_at":{"type":["string","null"],"format":"date-time"},"owner_employee_id":{"type":["string","null"]},"member_ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":200}},"required":["reason","member_ids"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"},"skipped":{"type":"string"}},"required":["created","skipped"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tasks/{task}":{"patch":{"operationId":"retention.update","summary":"PATCH /tasks/{task} {status} \u2014 resolve (status=done) or re-open (status=open) a task","tags":["Members & CRM"],"parameters":[{"name":"task","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","done"]}},"required":["status"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"member_name":{"type":["string","null"]},"owner_employee_id":{"type":["string","null"]},"type":{"type":"string"},"reason":{"type":"string"},"due_at":{"type":"string"},"status":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","member_id","member_name","owner_employee_id","type","reason","due_at","status","resolved_at","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/retention/playbook":{"get":{"operationId":"retentionPlaybook.index","summary":"GET /retention/playbook \u2014 every play with its live on/off state + fired count","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plays":{"type":"array","items":{}},"enabled_count":{"type":"integer"},"total":{"type":"integer"}},"required":["plays","enabled_count","total"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/retention/playbook/enable-recommended":{"post":{"operationId":"retentionPlaybook.enableRecommended","summary":"POST /retention/playbook/enable-recommended \u2014 switch on the whole recommended program","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"integer"}},"required":["enabled"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/retention/playbook/{key}/toggle":{"post":{"operationId":"retentionPlaybook.toggle","summary":"POST /retention/playbook/{key}/toggle {enabled} \u2014 turn one play on or off","tags":["Members & CRM"],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/retention/roi":{"get":{"operationId":"retention.roi","summary":"GET /retention/roi?from&to \u2014 the Control / ROI report (saved, revenue retained, save-rate)","tags":["Members & CRM"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"period":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"]},"interventions":{"type":"integer","minimum":0},"resolved":{"type":"integer","minimum":0},"saved":{"type":"integer","minimum":0},"save_rate":{"type":"integer"},"revenue_retained_minor":{"type":"integer"},"at_risk_now":{"type":"integer"},"nps":{"type":"integer"},"cancellations":{"type":"integer"},"by_rep":{"type":"array","items":{"type":"object","additionalProperties":{}}},"currency":{"type":"string","const":"SAR"}},"required":["period","interventions","resolved","saved","save_rate","revenue_retained_minor","at_risk_now","nps","cancellations","by_rep","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/complaints":{"get":{"operationId":"complaint.index","summary":"GET /complaints?status=open|resolved \u2014 the brand's complaints, newest first","tags":["Members & CRM"],"parameters":[{"name":"status","in":"query","schema":{"type":["string","null"],"enum":["open","resolved"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"complaints":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"member_name":{"type":["string","null"]},"category":{"type":"string"},"body":{"type":["string","null"]},"club_id":{"type":["string","null"]},"club_name":{},"image_url":{"type":["string","null"]},"source":{"type":"string","enum":["member","staff"]},"status":{"type":"string"},"created_at":{"type":"string"},"resolved_at":{"type":"string"}},"required":["id","member_id","member_name","category","body","club_id","club_name","image_url","source","status","created_at","resolved_at"]}},"open_count":{"type":"integer","minimum":0}},"required":["complaints","open_count"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/messages":{"get":{"operationId":"memberActivity.messages","summary":"List the current brand's message log for a member (newest first)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"channel":{"type":"string"},"subject":{"type":["string","null"]},"body":{"type":"string"},"sent_at":{"type":"string"}},"required":["id","member_id","channel","subject","body","sent_at"]}}},"required":["messages"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"memberActivity.logMessage","summary":"Record a message-log row (simulated send). This only writes the immutable log;\nreal delivery (templating + provider dispatch) is Module 17 and is deferred","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","enum":["sms","email","push","whatsapp"]},"subject":{"type":["string","null"],"maxLength":255},"body":{"type":"string"}},"required":["channel","body"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"channel":{"type":"string"},"subject":{"type":["string","null"]},"body":{"type":"string"},"sent_at":{"type":"string"}},"required":["id","member_id","channel","subject","body","sent_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/purchases":{"get":{"operationId":"memberPurchase.index","summary":"Retail purchase ledger for a member in the current brand (newest first)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"purchases":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"date":{"type":"string"},"status":{"type":"string"},"total_minor":{"type":"integer"},"items":{"type":"string"}},"required":["id","date","status","total_minor","items"]}}},"required":["purchases"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/referral-code":{"post":{"operationId":"referral.code","summary":"Mint (or return) the member's stable invite code","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"referrer_member_id":{"type":"string"},"code":{"type":"string"},"referee_member_id":{"type":["string","null"]},"status":{"type":"string"},"reward_minor":{"type":"integer"}},"required":["id","referrer_member_id","code","referee_member_id","status","reward_minor"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/pipeline":{"get":{"operationId":"crm.pipeline","description":"The terminal Won/Lost columns are capped to the most-recent TERMINAL_CAP cards\n(by updated_at, newest first) so they don't grow without bound; the working stages\n(new..second_visit_booked) are naturally small and stay uncapped. The returned `counts`\nalways report the true per-stage total (after the active-membership drop + filters).\nOptional filters narrow every stage BEFORE the cap is applied: ?q (case-insensitive\nmatch on member name OR lead_source), ?owner_id (owner_employee_id), ?source (exact\nlead_source).","summary":"Lead pipeline board: this brand's ProspectProfiles grouped by stage","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"pipeline":{"type":"string"},"counts":{"type":"string"},"terminal_cap":{"type":"integer","const":25}},"required":["pipeline","counts","terminal_cap"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/options":{"get":{"operationId":"crm.options","summary":"GET /crm/options \u2014 assignable salespeople (lead-routing eligible) + branches for the new-lead form","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"salespeople":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"club_ids":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]}},"required":["id","name","club_ids"]}},"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["salespeople","clubs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/leads":{"post":{"operationId":"crm.createLead","summary":"Capture a new lead. The Member is the cross-brand Golden Record (RFP Module 12),\nso we dedup it by National ID / mobile before minting a new one; the ProspectProfile\nis then ensured for THIS brand (brand_id auto-fills from TenantContext via BelongsToBrand)","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"mobile":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email"},"national_id":{"type":["string","null"]},"lead_source":{"type":["string","null"]},"referral_source":{"$ref":"#/components/schemas/ReferralSource"},"pipeline_stage":{"$ref":"#/components/schemas/PipelineStage"},"consent_marketing":{"type":["boolean","null"]},"owner_employee_id":{"type":["string","null"],"description":"RFP 11 \u2014 the front desk may hand-pick the owner + branch on creation; blank \u2192 auto round-robin / no branch."},"club_id":{"type":["string","null"]}},"required":["first_name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"pipeline_stage":{"type":"string"},"owner_employee_id":{"type":["string","null"]},"sla_due_at":{"type":"string"}},"required":["member_id","pipeline_stage","owner_employee_id","sla_due_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/leads/{member}/free-visit":{"post":{"operationId":"crm.freeVisit","summary":"Book a free/trial visit for this lead (RFP Module 11): a scheduled appointment hosted by a staff\nmember. First booking \u2192 Free visit booked; a re-book (after a no-show) \u2192 2nd visit booked. Attendance\n(showed / no-show) is marked later via freeVisitAttendance(), which advances the pipeline stage","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scheduled_at":{"type":"string","format":"date-time","description":"A trial visit can't be booked in the past (2-minute grace for clock skew)."},"host_employee_id":{"type":["string","null"]},"notes":{"type":["string","null"],"maxLength":500}},"required":["scheduled_at"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"visit":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"lead_name":{"type":"string"},"host_employee_id":{"type":["string","null"]},"host_name":{"type":["string","null"]},"scheduled_at":{"type":"string"},"visit_number":{"type":"integer"},"status":{"type":"string"},"notes":{"type":["string","null"]}},"required":["id","member_id","lead_name","host_employee_id","host_name","scheduled_at","visit_number","status","notes"]},"pipeline_stage":{"type":"string"}},"required":["visit","pipeline_stage"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/free-visits":{"get":{"operationId":"crm.freeVisits","summary":"GET /crm/free-visits?from&to&status \u2014 the free-visit agenda (leads' trial visits)","tags":["Members & CRM"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"status","in":"query","schema":{"type":["string","null"],"enum":["booked","showed","no_show","cancelled"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"visits":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"lead_name":{"type":"string"},"host_employee_id":{"type":["string","null"]},"host_name":{"type":["string","null"]},"scheduled_at":{"type":"string"},"visit_number":{"type":"integer"},"status":{"type":"string"},"notes":{"type":["string","null"]}},"required":["id","member_id","lead_name","host_employee_id","host_name","scheduled_at","visit_number","status","notes"]}}},"required":["visits"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/free-visits/{visit}/attendance":{"post":{"operationId":"crm.freeVisitAttendance","summary":"POST /crm/free-visits/{visit}/attendance \u2014 mark showed / no-show; advances the lead's stage","tags":["Members & CRM"],"parameters":[{"name":"visit","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["showed","no_show"]}},"required":["status"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"visit":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"lead_name":{"type":"string"},"host_employee_id":{"type":["string","null"]},"host_name":{"type":["string","null"]},"scheduled_at":{"type":"string"},"visit_number":{"type":"integer"},"status":{"type":"string"},"notes":{"type":["string","null"]}},"required":["id","member_id","lead_name","host_employee_id","host_name","scheduled_at","visit_number","status","notes"]},"pipeline_stage":{"type":"string"}},"required":["visit","pipeline_stage"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/leads/bulk-assign":{"post":{"operationId":"crm.bulkAssign","summary":"Bulk-reassign leads to one salesperson (RFP 11-8.0). Manager-only (crm.view_all) so a rep can't\nsweep up other reps' leads; each moved lead is logged + the new owner notified","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employee_id":{"type":"string"},"member_ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":200}},"required":["employee_id","member_ids"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"assigned":{"type":"integer"}},"required":["assigned"]}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Only managers can bulk-assign leads."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/leads/{member}/stage":{"patch":{"operationId":"crm.moveStage","summary":"Move a lead to another pipeline stage (updates its ProspectProfile)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stage":{"$ref":"#/components/schemas/PipelineStage"},"lost_reason_code":{"type":["string","null"],"description":"A mandatory loss reason is required only when dropping the lead (RFP 11-10.0).","enum":[""]}},"required":["stage"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"pipeline_stage":{"type":"string"},"lost_reason_code":{"type":["string","null"]},"sla_due_at":{"type":"string"},"engagement_score":{"type":["number","null"]}},"required":["member_id","pipeline_stage","lost_reason_code","sla_due_at","engagement_score"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/leads/{member}/owner":{"patch":{"operationId":"crm.assign","summary":"Reassign a lead's owner (RFP 11-8.0). The target employee must be active and\nassigned to a club in this brand; the prospect itself is brand-scoped","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employee_id":{"type":"string"}},"required":["employee_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"owner_employee_id":{"type":["string","null"]}},"required":["member_id","owner_employee_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/leads/{member}/escalate":{"post":{"operationId":"crm.escalate","summary":"POST /crm/leads/{member}/escalate \u2014 escalate a customer case to a manager/other employee.\nReassigns the owner and records an Escalation touch carrying the previous owner + reason, so the\ntimeline is the \"Assign / Escalation\" status history (Assign-To, Previous-Owner, Status History, #5)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employee_id":{"type":"string"},"note":{"type":["string","null"],"maxLength":500}},"required":["employee_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"owner_employee_id":{"type":["string","null"]}},"required":["member_id","owner_employee_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/lost-reasons":{"get":{"operationId":"crm.lostReasons","summary":"Loss analytics (RFP 11-10.0): counts of lost prospects in this brand grouped by\nmandatory loss reason. Brand-scoped via the ProspectProfile global scope","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"reasons":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"count":{"type":"integer"}},"required":["code","count"]}},"total":{"type":"integer"}},"required":["reasons","total"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/daily-tasks":{"get":{"operationId":"crm.dailyTasks","summary":"A rep's daily follow-up worklist (RFP 11): the open leads they own that need attention today,\nbucketed into overdue / due-today follow-ups (from the contact log) and never-contacted leads\nwhose first-response SLA has lapsed. Scoped to the caller's own leads unless they hold\ncrm.view_all, in which case ?owner_id narrows to one rep (default: everyone's)","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"overdue":{"type":"array","items":{"type":"object","properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"mobile":{"type":"string"},"email":{"type":"string"}},"required":["id","name","mobile","email"]},"pipeline_stage":{"type":"string"},"owner":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"club":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"next_follow_up_at":{"type":"string"},"last_contacted_at":{"type":"string"},"sla_due_at":{"type":"string"},"lead_source":{"type":"string"}},"required":["member","pipeline_stage","owner","club","next_follow_up_at","last_contacted_at","sla_due_at","lead_source"]}},"today":{"type":"array","items":{"type":"object","properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"mobile":{"type":"string"},"email":{"type":"string"}},"required":["id","name","mobile","email"]},"pipeline_stage":{"type":"string"},"owner":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"club":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"next_follow_up_at":{"type":"string"},"last_contacted_at":{"type":"string"},"sla_due_at":{"type":"string"},"lead_source":{"type":"string"}},"required":["member","pipeline_stage","owner","club","next_follow_up_at","last_contacted_at","sla_due_at","lead_source"]}},"sla_overdue":{"type":"array","items":{"type":"object","properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"mobile":{"type":"string"},"email":{"type":"string"}},"required":["id","name","mobile","email"]},"pipeline_stage":{"type":"string"},"owner":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"club":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"next_follow_up_at":{"type":"string"},"last_contacted_at":{"type":"string"},"sla_due_at":{"type":"string"},"lead_source":{"type":"string"}},"required":["member","pipeline_stage","owner","club","next_follow_up_at","last_contacted_at","sla_due_at","lead_source"]}},"counts":{"type":"object","properties":{"overdue":{"type":"integer"},"today":{"type":"integer"},"sla_overdue":{"type":"integer"},"total":{"type":"string"}},"required":["overdue","today","sla_overdue","total"]},"scoped_to_owner":{"type":"string"}},"required":["overdue","today","sla_overdue","counts","scoped_to_owner"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/crm/settings":{"get":{"operationId":"crm.settings","summary":"GET /crm/settings \u2014 the brand's CRM automation settings (managers only)","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"auto_cold_enabled":{"type":"boolean"},"auto_cold_days":{"type":"integer"},"followup_reminders_enabled":{"type":"boolean"}},"required":["auto_cold_enabled","auto_cold_days","followup_reminders_enabled"]}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Only managers can view CRM settings."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"crm.updateSettings","summary":"PATCH /crm/settings \u2014 update auto-cold + follow-up reminder settings (managers only)","tags":["Members & CRM"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"auto_cold_enabled":{"type":"boolean"},"auto_cold_days":{"type":"integer","minimum":7,"maximum":365},"followup_reminders_enabled":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"auto_cold_enabled":{"type":"boolean"},"auto_cold_days":{"type":"integer"},"followup_reminders_enabled":{"type":"boolean"}},"required":["auto_cold_enabled","auto_cold_days","followup_reminders_enabled"]}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Only managers can change CRM settings."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/notes":{"get":{"operationId":"crm.notes","summary":"List a member's activity notes, newest first (brand-scoped)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"notes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"author":{"type":"string"},"kind":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","member_id","author","kind","body","created_at"]}}},"required":["notes"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"crm.storeNote","summary":"Log an activity note against a member (brand-scoped)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["note","call","visit","whatsapp"]},"body":{"type":"string"}},"required":["kind","body"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"author":{"type":"string"},"kind":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","member_id","author","kind","body","created_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/segments":{"get":{"operationId":"segment.index","tags":["Members & CRM"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"segments":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"filters":{"type":"array","items":{}},"member_count":{"type":["integer","null"],"minimum":0},"created_at":{"type":"string"}},"required":["id","name","description","filters","member_count","created_at"]}}},"required":["segments"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"segment.store","tags":["Members & CRM"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"filters":{"type":"array","items":{}},"member_count":{"type":["integer","null"],"minimum":0},"created_at":{"type":"string"}},"required":["id","name","description","filters","member_count","created_at"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/segments/preview":{"post":{"operationId":"segment.preview","summary":"Live preview of an UNSAVED filter set: count + a 10-member sample (for the builder)","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filters":{"type":"array","items":{"type":"string"}}},"required":["filters"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"sample":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["count","sample"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/segments/{segment}/members":{"get":{"operationId":"segment.members","summary":"Resolve a saved segment to its members (reuse in reports / drill-down)","tags":["Members & CRM"],"parameters":[{"name":"segment","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"mobile":{"type":["string","null"]},"gender":{"type":"string"}},"required":["id","name","mobile","gender"]}}},"required":["count","members"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/segments/{segment}":{"patch":{"operationId":"segment.update","tags":["Members & CRM"],"parameters":[{"name":"segment","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"filters":{"type":"array","items":{}},"member_count":{"type":["integer","null"],"minimum":0},"created_at":{"type":"string"}},"required":["id","name","description","filters","member_count","created_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"segment.destroy","tags":["Members & CRM"],"parameters":[{"name":"segment","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/leads/{member}":{"get":{"operationId":"lead.show","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"profile_type":{"type":"string"},"pipeline_stage":{"type":"string"},"utm":{"type":["array","null"],"items":{}}},"required":["member_id","profile_type","pipeline_stage","utm"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/leads/{member}/convert":{"post":{"operationId":"lead.convert","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"profile_type":{"type":"string","const":"member"}},"required":["member_id","profile_type"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members":{"get":{"operationId":"member.index","summary":"Search this brand's members by name / National ID / Iqama / mobile (RFP 12 \u2014 Golden Record)","tags":["Members & CRM"],"parameters":[{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"name","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"mobile","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"email","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"active","in":"query","schema":{"type":["boolean","null"]}},{"name":"gender","in":"query","schema":{"$ref":"#/components/schemas/Gender"}},{"name":"membership","in":"query","description":"`prospect` = alias for enrolled=false (never had a contract) \u2014 kept so external\ncallers can express all three states through one param.","schema":{"type":["string","null"],"enum":["active","inactive","prospect"]}},{"name":"enrolled","in":"query","description":"false \u2192 \"Registered (not enrolled)\": app signups / leads with no membership contract yet.","schema":{"type":["boolean","null"]}},{"name":"pool","in":"query","description":"all \u2192 enrolled AND prospects in one list (POS lookup); wins over `enrolled`.","schema":{"type":["string","null"],"enum":["enrolled","prospects","all"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"national_id":{"type":["string","null"]},"mobile":{"type":["string","null"]},"email":{"type":["string","null"]},"gender":{"type":"string"},"value_segment":{"type":["string","null"]},"has_active_membership":{"type":"boolean"},"plan":{"type":"string"},"follow_up_due_at":{"type":["string","null"]}},"required":["id","name","name_ar","national_id","mobile","email","gender","value_segment","has_active_membership","plan","follow_up_due_at"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer","const":20},"total":{"type":"string"},"last_page":{"type":["object","null"]}},"required":["page","per_page","total","last_page"]}},"required":["members","meta"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"member.store","summary":"Quick-create a walk-in member (RFP 06 POS): a lightweight profile (no membership) so the cashier\ncan register a walk-in and immediately sell to them. Dedupes per-brand by national-id / iqama /\nmobile \u2014 a match returns the existing member (`existing: true`) rather than a duplicate","tags":["Members & CRM"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","maxLength":120},"last_name":{"type":["string","null"],"maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"gender":{"$ref":"#/components/schemas/Gender"},"mobile":{"type":["string","null"],"maxLength":30},"national_id":{"type":["string","null"]},"iqama_number":{"type":["string","null"],"maxLength":30},"email":{"type":["string","null"],"format":"email","maxLength":190},"dob":{"type":["string","null"],"format":"date-time"},"referral_source":{"$ref":"#/components/schemas/ReferralSource"}},"required":["first_name","gender"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"national_id":{"type":["string","null"]},"mobile":{"type":["string","null"]},"email":{"type":["string","null"]},"gender":{"type":"string"},"value_segment":{"type":["string","null"]},"has_active_membership":{"type":"boolean"},"plan":{"type":"null"}},"required":["id","name","name_ar","national_id","mobile","email","gender","value_segment","has_active_membership","plan"]},"existing":{"type":"boolean"}},"required":["member","existing"]}}}},"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"national_id":{"type":["string","null"]},"mobile":{"type":["string","null"]},"email":{"type":["string","null"]},"gender":{"type":"string"},"value_segment":{"type":["string","null"]},"has_active_membership":{"type":"boolean"},"plan":{"type":"null"}},"required":["id","name","name_ar","national_id","mobile","email","gender","value_segment","has_active_membership","plan"]},"existing":{"type":"boolean"}},"required":["member","existing"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/export":{"get":{"operationId":"member.export","summary":"Export the full filtered directory as a real .xlsx (RFP 12 \u2014 operational reporting).\nHonours every list filter but ignores pagination, so staff export exactly what they\nsee filtered. National IDs respect the same PDPL masking as the on-screen table","tags":["Members & CRM"],"parameters":[{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"name","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"mobile","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"email","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"active","in":"query","schema":{"type":["boolean","null"]}},{"name":"gender","in":"query","schema":{"$ref":"#/components/schemas/Gender"}},{"name":"membership","in":"query","schema":{"type":["string","null"],"enum":["active","inactive","prospect"]}},{"name":"enrolled","in":"query","schema":{"type":["boolean","null"]}},{"name":"pool","in":"query","schema":{"type":["string","null"],"enum":["enrolled","prospects","all"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}":{"get":{"operationId":"member.show","summary":"Member 360 \u2014 identity + membership history + invoices + credential + pipeline, all brand-scoped","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contact":{"type":"object","properties":{"last_contacted_at":{"type":"string"},"next_follow_up_at":{"type":"string"}},"required":["last_contacted_at","next_follow_up_at"]},"member":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"national_id":{"type":["string","null"]},"iqama_number":{"type":["string","null"]},"mobile":{"type":"string"},"email":{"type":"string"},"gender":{"type":"string"},"dob":{"type":"string"},"is_minor":{"type":"string"},"nationality":{"type":"string"},"value_segment":{"type":"string"},"referral_source":{"type":"string"},"home_club_id":{"type":"string"},"home_club_name":{},"account_owner_employee_id":{"type":"string"},"account_owner_name":{"type":["string","null"]},"avatar_url":{"type":"string","description":"PDPL-safe self-chosen avatar (never the identity photo, esp. for female members)."},"address":{"type":"string"},"language_pref":{"type":"string"},"dob_calendar_pref":{"type":"string"},"last_check_in_at":{"type":"string"},"last_login_at":{"type":"string"},"churn_score":{"type":"string"},"churn":{"type":["object","null"],"additionalProperties":{}},"guardian_id":{"type":"string"},"guardian_name":{"type":["string","null"]},"emergency_contact_name":{"type":"string"},"emergency_contact_phone":{"type":"string"},"emergency_contact_relation":{"type":"string"},"health_waiver_received":{"type":"boolean","description":"Health waiver / PAR-Q (RFP 05 #15). The uploaded PAR-Q file (if any) so staff can view or remove it."},"health_waiver_at":{"type":"string"},"health_waiver_document":{"type":["object","null"],"properties":{"id":{"type":"string"},"original_name":{"type":"string"}},"required":["id","original_name"]},"dues_overdue_at":{"type":"string","description":"Outstanding-dues marker (RFP 05 #3) \u2014 set since this date when in arrears."}},"required":["id","name","name_ar","national_id","iqama_number","mobile","email","gender","dob","is_minor","nationality","value_segment","referral_source","home_club_id","home_club_name","account_owner_employee_id","account_owner_name","avatar_url","address","language_pref","dob_calendar_pref","last_check_in_at","last_login_at","churn_score","churn","guardian_id","guardian_name","emergency_contact_name","emergency_contact_phone","emergency_contact_relation","health_waiver_received","health_waiver_at","health_waiver_document","dues_overdue_at"]},"membership":{"anyOf":[{"type":"object","properties":{"contract_id":{"type":"string"},"plan":{"type":"string"},"state":{"type":"string"},"activation_date":{"type":["string","null"]},"expiry_date":{"type":["string","null"]},"paid_days_remaining":{"type":"integer"}},"required":["contract_id","plan","state","activation_date","expiry_date","paid_days_remaining"]},{"type":"null"},{"type":"object","properties":{"contract_id":{"type":"string"},"plan":{"type":"string"},"state":{"type":"string"},"activation_date":{"type":"string"},"expiry_date":{"type":"string"},"paid_days_remaining":{"type":"integer"}},"required":["contract_id","plan","state","activation_date","expiry_date","paid_days_remaining"]}]},"contracts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"plan":{"type":"string"},"state":{"type":"string","description":"Raw values (not the enum cast) so a legacy/unknown value can't 500 the 360 page."},"type":{"type":"string"},"sale_date":{"type":"string"},"activation_date":{"type":"string"},"expiry_date":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"corporate_partner_id":{"type":["string","null"],"description":"Corporate/family affiliation (RFP 20) \u2014 surfaces a B2B or family-plan contract."},"corporate_partner_name":{},"group_id":{"type":["string","null"]}},"required":["id","plan","state","type","sale_date","activation_date","expiry_date","amount_minor","currency","corporate_partner_id","corporate_partner_name","group_id"]}},"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"uuid":{"type":"string"},"type":{"type":"string","description":"Raw value so an unexpected type can't 500 the 360; credit_note = a refund, not a charge."},"gross_minor":{"type":"integer"},"currency":{"type":"string"},"issued_at":{"type":"string"}},"required":["id","uuid","type","gross_minor","currency","issued_at"]}},"credential":{"type":["object","null"],"properties":{"status":{"type":"string"},"type":{"type":"string"},"valid_until":{"type":"string"}},"required":["status","type","valid_until"]},"prospect":{"type":["object","null"],"properties":{"pipeline_stage":{"type":"string"},"lead_source":{"type":["string","null"]}},"required":["pipeline_stage","lead_source"]}},"required":["contact","member","membership","contracts","invoices","credential","prospect"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/membership":{"get":{"operationId":"member.membership","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contract_id":{"type":"string"},"plan":{"type":"string"},"state":{"type":"string"},"activation_date":{"type":["string","null"]},"expiry_date":{"type":["string","null"]},"paid_days_remaining":{"type":"integer"}},"required":["contract_id","plan","state","activation_date","expiry_date","paid_days_remaining"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/credential/qr":{"get":{"operationId":"member.qr","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"qr_token":{"type":"string"},"expires_in":{"type":"integer"}},"required":["qr_token","expires_in"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/invoices":{"get":{"operationId":"member.invoices","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"uuid":{"type":"string"},"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"gross_minor":{"type":"integer"},"currency":{"type":"string"},"issued_at":{"type":"string"}},"required":["id","uuid","net_minor","vat_minor","gross_minor","currency","issued_at"]}}},"required":["invoices"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/wallet":{"get":{"operationId":"wallet.show","summary":"Balance + statement for a member's wallet","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"balance_minor":{"type":"integer"},"currency":{"type":"string"},"ledger":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"amount_minor":{"type":"integer"},"balance_after_minor":{"type":"integer"},"source":{"type":["string","null"]},"description":{"type":["string","null"]},"description_ar":{"description":"Localised label by transaction type; falls back to the (English) description.","anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"type":"string"}},"required":["id","type","amount_minor","balance_after_minor","source","description","description_ar","created_at"]}}},"required":["balance_minor","currency","ledger"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/wallet/topup":{"post":{"operationId":"wallet.topup","summary":"Top up a member's wallet. Cash credits the ledger immediately; card opens a hosted-page\ncharge (purpose=wallet_topup) and is credited only once it settles (listener)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer","minimum":1},"source":{"type":"string","enum":["card","cash"]}},"required":["amount_minor","source"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"redirect_url":{"type":["string","null"]}},"required":["payment_id","redirect_url"]}}}},"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"balance_minor":{"type":"integer"}},"required":["balance_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/wallet/spend":{"post":{"operationId":"wallet.spend","summary":"Spend wallet credit on a club service. Insufficient balance is a 422, not a 500","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer","minimum":1},"description":{"type":["string","null"]}},"required":["amount_minor"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"balance_minor":{"type":"integer"}},"required":["balance_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/wallet/adjust":{"post":{"operationId":"wallet.adjust","summary":"Manual adjustment (+ credit / \u2212 debit) with an audit reason (RFP 03 #19)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer"},"reason":{"type":"string"}},"required":["amount_minor","reason"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"balance_minor":{"type":"integer"}},"required":["balance_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/payment-methods":{"get":{"operationId":"paymentMethod.index","summary":"List a member's active stored cards (token/customer id are never exposed)","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gateway":{"type":"string"},"scheme":{"type":["string","null"]},"last4":{"type":["string","null"]},"exp_month":{"type":["string","null"]},"exp_year":{"type":["string","null"]},"is_primary":{"type":"boolean"},"is_backup":{"type":"boolean"}},"required":["id","gateway","scheme","last4","exp_month","exp_year","is_primary","is_backup"]}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/members/{member}/payment-methods/charge":{"post":{"operationId":"paymentMethod.charge","summary":"Recurring / tokenised charge against the member's stored card (#3 resilience demo).\nCharges the primary card; if that result is Failed and a backup exists, retries once on\nthe backup (auto-fallback). Persists the Payment and reports which card carried it","tags":["Members & CRM"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer","minimum":1},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"description":{"type":["string","null"]}},"required":["amount_minor"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"status":{"type":"string"},"card_last4":{"type":["string","null"]},"used":{"type":"string","enum":["none","backup"]}},"required":["payment_id","status","card_last4","used"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/plans":{"get":{"operationId":"catalogue.plans","tags":["Memberships & Contracts"],"parameters":[{"name":"channel","in":"query","schema":{"$ref":"#/components/schemas/SalesChannel"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"duration_months":{"type":["integer","null"]},"duration_days":{"type":["integer","null"]},"billing_mode":{"type":"string"},"gender":{"type":"string"},"price_minor":{"type":"null"},"currency":{"type":"string","const":"SAR"}},"required":["id","code","name","name_ar","duration_months","duration_days","billing_mode","gender","price_minor","currency"]}}},"required":["plans"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/plans/{plan}/terms":{"get":{"operationId":"catalogue.planTerms","summary":"GET /plans/{plan}/terms \u2014 the plan's resolved terms & conditions (auto commercial clauses + notes)","tags":["Memberships & Contracts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/cooling-off-refund":{"post":{"operationId":"refund.request","summary":"Raise a cooling-off refund request \u2192 Operations-Director approval","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason_code":{"type":"string","maxLength":64},"destination":{"type":"string","enum":["wallet","bank"]},"iban":{"type":["string","null"],"maxLength":34},"requested_by":{"type":["string","null"],"maxLength":120}},"required":["reason_code","destination"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"approval_id":{"type":"string"},"status":{"type":"string"},"amount_minor":{"anyOf":[{"type":"string"},{"type":"integer","enum":[0]}]}},"required":["approval_id","status","amount_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/agreements/{agreement}":{"get":{"operationId":"agreement.show","summary":"Download the generated bilingual agreement PDF (brand-scoped)","tags":["Memberships & Contracts"],"parameters":[{"name":"agreement","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/plans":{"get":{"operationId":"plan.index","summary":"List all of the brand's plans (active and inactive) with their price points","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"plan_family":{"type":"string"},"billing_mode":{"type":"string"},"gender":{"type":"string"},"duration_months":{"type":["integer","null"]},"duration_days":{"type":["integer","null"]},"sessions_per_week":{"type":["integer","null"]},"channels":{"type":"array","items":{}},"is_active":{"type":"boolean"},"membership_type_id":{"type":["string","null"],"description":"RFP 01 plan-config & taxonomy."},"renew_policy_id":{"type":["string","null"]},"pre_sale_config_id":{"type":["string","null"]},"age_min":{"type":["integer","null"]},"age_max":{"type":["integer","null"]},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"is_day_pass":{"type":"boolean"},"access_template":{"type":["string","null"]},"zone_scope":{"type":["array","null"],"items":{}},"amenities":{"type":["array","null"],"description":"#5/#6/#7 \u2014 plan amenities/benefits (marketing features).","items":{}},"terms_notes":{"type":["string","null"],"description":"RFP 04 \u2014 per-plan terms notes."},"terms_notes_ar":{"type":["string","null"]},"is_available":{"type":"boolean","description":"#2/#16 computed availability: active AND within any seasonal window today."},"prices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"scope_type":{"type":"string"},"scope_id":{"type":["string","null"]},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"channel":{"anyOf":[{"$ref":"#/components/schemas/SalesChannel"},{"type":"null"}]},"segment":{"type":"string"}},"required":["id","scope_type","scope_id","amount_minor","currency","channel","segment"]}}},"required":["id","code","name","name_ar","image_url","plan_family","billing_mode","gender","duration_months","duration_days","sessions_per_week","channels","is_active","membership_type_id","renew_policy_id","pre_sale_config_id","age_min","age_max","valid_from","valid_to","is_day_pass","access_template","zone_scope","amenities","terms_notes","terms_notes_ar","is_available","prices"]}}},"required":["plans"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"plan.store","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"image_url":{"type":["string","null"],"maxLength":1000},"plan_family":{"$ref":"#/components/schemas/PlanFamily"},"billing_mode":{"$ref":"#/components/schemas/BillingMode"},"duration_months":{"type":["integer","null"],"minimum":1,"maximum":120},"duration_days":{"type":["integer","null"],"description":"Days-based validity for a day/multi-day pass (e.g. a 7-day pass). NULL = month-based.","minimum":1,"maximum":366},"sessions_per_week":{"type":["integer","null"],"description":"Weekly class-session entitlement (0 / null = unlimited).","minimum":0,"maximum":200},"gender":{"$ref":"#/components/schemas/Gender"},"amount_minor":{"type":"integer","minimum":0},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"membership_type_id":{"type":["string","null"],"description":"RFP 01 plan-config & taxonomy (all optional, additive)."},"renew_policy_id":{"type":["string","null"]},"pre_sale_config_id":{"type":["string","null"]},"age_min":{"type":["integer","null"],"minimum":0,"maximum":120},"age_max":{"type":["integer","null"],"minimum":0,"maximum":120},"valid_from":{"type":["string","null"],"format":"date-time"},"valid_to":{"type":["string","null"],"format":"date-time"},"is_day_pass":{"type":["boolean","null"]},"access_template":{"type":["string","null"],"maxLength":50},"zone_scope":{"type":["array","null"],"items":{"type":"string"}},"eligibility_segment_id":{"type":["string","null"],"description":"RFP 1.7 \u2014 restrict who can buy this package to a saved Segment (NULL = open to all)."},"terms_notes":{"type":["string","null"],"description":"RFP 04 \u2014 per-plan terms & conditions notes (bilingual free text).","maxLength":8000},"terms_notes_ar":{"type":["string","null"],"maxLength":8000},"channels":{"type":"array","items":{"$ref":"#/components/schemas/SalesChannel"},"minItems":1},"amenities":{"type":["array","null"],"description":"#5/#6/#7 \u2014 plan amenities/benefits (marketing features), optional & additive.","items":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"included":{"type":["boolean","null"]}}}}},"required":["code","name","plan_family","billing_mode","amount_minor","channels"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"plan_family":{"type":"string"},"billing_mode":{"type":"string"},"gender":{"type":"string"},"duration_months":{"type":["integer","null"]},"duration_days":{"type":["integer","null"]},"sessions_per_week":{"type":["integer","null"]},"channels":{"type":"array","items":{}},"is_active":{"type":"boolean"},"membership_type_id":{"type":["string","null"],"description":"RFP 01 plan-config & taxonomy."},"renew_policy_id":{"type":["string","null"]},"pre_sale_config_id":{"type":["string","null"]},"age_min":{"type":["integer","null"]},"age_max":{"type":["integer","null"]},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"is_day_pass":{"type":"boolean"},"access_template":{"type":["string","null"]},"zone_scope":{"type":["array","null"],"items":{}},"amenities":{"type":["array","null"],"description":"#5/#6/#7 \u2014 plan amenities/benefits (marketing features).","items":{}},"terms_notes":{"type":["string","null"],"description":"RFP 04 \u2014 per-plan terms notes."},"terms_notes_ar":{"type":["string","null"]},"is_available":{"type":"boolean","description":"#2/#16 computed availability: active AND within any seasonal window today."},"prices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"scope_type":{"type":"string"},"scope_id":{"type":["string","null"]},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"channel":{"anyOf":[{"$ref":"#/components/schemas/SalesChannel"},{"type":"null"}]},"segment":{"type":"string"}},"required":["id","scope_type","scope_id","amount_minor","currency","channel","segment"]}}},"required":["id","code","name","name_ar","image_url","plan_family","billing_mode","gender","duration_months","duration_days","sessions_per_week","channels","is_active","membership_type_id","renew_policy_id","pre_sale_config_id","age_min","age_max","valid_from","valid_to","is_day_pass","access_template","zone_scope","amenities","terms_notes","terms_notes_ar","is_available","prices"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/plans/upload":{"post":{"operationId":"plan.upload","summary":"RFP 01 \u2014 upload a plan card image; returns a public object-store URL (mirrors add-ons)","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","maxLength":25600}},"required":["file"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/plans/{plan}":{"patch":{"operationId":"plan.update","tags":["Memberships & Contracts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"image_url":{"type":["string","null"],"maxLength":1000},"duration_months":{"type":["integer","null"],"minimum":1,"maximum":120},"duration_days":{"type":["integer","null"],"minimum":1,"maximum":366},"sessions_per_week":{"type":["integer","null"],"minimum":0,"maximum":200},"is_active":{"type":"boolean"},"gender":{"description":"Gender restriction (RFP 09 #3): null = open to all; male/female = gender-only plan.","$ref":"#/components/schemas/Gender"},"plan_family":{"description":"RFP 01 plan-config & taxonomy (all optional, additive).","$ref":"#/components/schemas/PlanFamily"},"billing_mode":{"$ref":"#/components/schemas/BillingMode"},"membership_type_id":{"type":["string","null"]},"renew_policy_id":{"type":["string","null"]},"pre_sale_config_id":{"type":["string","null"]},"age_min":{"type":["integer","null"],"minimum":0,"maximum":120},"age_max":{"type":["integer","null"],"minimum":0,"maximum":120},"valid_from":{"type":["string","null"],"format":"date-time"},"valid_to":{"type":["string","null"],"format":"date-time"},"is_day_pass":{"type":"boolean"},"access_template":{"type":["string","null"],"maxLength":50},"zone_scope":{"type":["array","null"],"items":{"type":"string"}},"eligibility_segment_id":{"type":["string","null"],"description":"RFP 1.7 \u2014 restrict who can buy this package to a saved Segment (NULL = open to all)."},"terms_notes":{"type":["string","null"],"description":"RFP 04 \u2014 per-plan terms & conditions notes (bilingual free text).","maxLength":8000},"terms_notes_ar":{"type":["string","null"],"maxLength":8000},"channels":{"type":"array","items":{"$ref":"#/components/schemas/SalesChannel"},"minItems":1},"amenities":{"type":["array","null"],"description":"#5/#6/#7 \u2014 plan amenities/benefits (marketing features), optional & additive.","items":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"included":{"type":["boolean","null"]}}}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"plan_family":{"type":"string"},"billing_mode":{"type":"string"},"gender":{"type":"string"},"duration_months":{"type":["integer","null"]},"duration_days":{"type":["integer","null"]},"sessions_per_week":{"type":["integer","null"]},"channels":{"type":"array","items":{}},"is_active":{"type":"boolean"},"membership_type_id":{"type":["string","null"],"description":"RFP 01 plan-config & taxonomy."},"renew_policy_id":{"type":["string","null"]},"pre_sale_config_id":{"type":["string","null"]},"age_min":{"type":["integer","null"]},"age_max":{"type":["integer","null"]},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"is_day_pass":{"type":"boolean"},"access_template":{"type":["string","null"]},"zone_scope":{"type":["array","null"],"items":{}},"amenities":{"type":["array","null"],"description":"#5/#6/#7 \u2014 plan amenities/benefits (marketing features).","items":{}},"terms_notes":{"type":["string","null"],"description":"RFP 04 \u2014 per-plan terms notes."},"terms_notes_ar":{"type":["string","null"]},"is_available":{"type":"boolean","description":"#2/#16 computed availability: active AND within any seasonal window today."},"prices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"scope_type":{"type":"string"},"scope_id":{"type":["string","null"]},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"channel":{"anyOf":[{"$ref":"#/components/schemas/SalesChannel"},{"type":"null"}]},"segment":{"type":"string"}},"required":["id","scope_type","scope_id","amount_minor","currency","channel","segment"]}}},"required":["id","code","name","name_ar","image_url","plan_family","billing_mode","gender","duration_months","duration_days","sessions_per_week","channels","is_active","membership_type_id","renew_policy_id","pre_sale_config_id","age_min","age_max","valid_from","valid_to","is_day_pass","access_template","zone_scope","amenities","terms_notes","terms_notes_ar","is_available","prices"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/plans/{plan}/prices":{"post":{"operationId":"plan.storePrice","tags":["Memberships & Contracts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"scope_type":{"$ref":"#/components/schemas/PriceScopeType"},"scope_id":{"type":["string","null"],"description":"Which city / region / country / tier / branch this price targets (the scope's target).","maxLength":120},"amount_minor":{"type":"integer","minimum":0},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"channel":{"$ref":"#/components/schemas/SalesChannel"},"segment":{"description":"Audience this price is for (corporate/new/returning\u2026); null = any audience.","$ref":"#/components/schemas/MemberSegment"},"effective_from":{"type":["string","null"],"format":"date-time","description":"Effective-dated window (RFP 01 #61 scheduled price changes / #60 concurrent points).\nPersisted to valid_from/valid_to \u2014 the columns PricingResolver actually filters on \u2014\nso a future-dated price coexists with today's and only wins once its window opens."},"effective_to":{"type":["string","null"],"format":"date-time"}},"required":["scope_type","amount_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/plans/{plan}/prices/{price}":{"patch":{"operationId":"plan.updatePrice","summary":"Edit an existing price point (amount / scope / channel / effective window)","tags":["Memberships & Contracts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}},{"name":"price","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"scope_type":{"$ref":"#/components/schemas/PriceScopeType"},"scope_id":{"type":["string","null"],"maxLength":120},"amount_minor":{"type":"integer","minimum":0},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"channel":{"$ref":"#/components/schemas/SalesChannel"},"segment":{"$ref":"#/components/schemas/MemberSegment"},"effective_from":{"type":["string","null"],"format":"date-time"},"effective_to":{"type":["string","null"],"format":"date-time"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"plan.destroyPrice","tags":["Memberships & Contracts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}},{"name":"price","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/plans/{plan}/copy":{"post":{"operationId":"plan.copy","summary":"#46 Plan Copy \u2014 deep-clone a plan and all its current price points into a new,\ninactive plan under a brand-unique code. Lets HO duplicate a template plan and\ntweak it before going live (the copy is created is_active=false)","tags":["Memberships & Contracts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":["string","null"],"maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120}},"required":["code"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"plan_family":{"type":"string"},"billing_mode":{"type":"string"},"gender":{"type":"string"},"duration_months":{"type":["integer","null"]},"duration_days":{"type":["integer","null"]},"sessions_per_week":{"type":["integer","null"]},"channels":{"type":"array","items":{}},"is_active":{"type":"boolean"},"membership_type_id":{"type":["string","null"],"description":"RFP 01 plan-config & taxonomy."},"renew_policy_id":{"type":["string","null"]},"pre_sale_config_id":{"type":["string","null"]},"age_min":{"type":["integer","null"]},"age_max":{"type":["integer","null"]},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"is_day_pass":{"type":"boolean"},"access_template":{"type":["string","null"]},"zone_scope":{"type":["array","null"],"items":{}},"amenities":{"type":["array","null"],"description":"#5/#6/#7 \u2014 plan amenities/benefits (marketing features).","items":{}},"terms_notes":{"type":["string","null"],"description":"RFP 04 \u2014 per-plan terms notes."},"terms_notes_ar":{"type":["string","null"]},"is_available":{"type":"boolean","description":"#2/#16 computed availability: active AND within any seasonal window today."},"prices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"scope_type":{"type":"string"},"scope_id":{"type":["string","null"]},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"channel":{"anyOf":[{"$ref":"#/components/schemas/SalesChannel"},{"type":"null"}]},"segment":{"type":"string"}},"required":["id","scope_type","scope_id","amount_minor","currency","channel","segment"]}}},"required":["id","code","name","name_ar","image_url","plan_family","billing_mode","gender","duration_months","duration_days","sessions_per_week","channels","is_active","membership_type_id","renew_policy_id","pre_sale_config_id","age_min","age_max","valid_from","valid_to","is_day_pass","access_template","zone_scope","amenities","terms_notes","terms_notes_ar","is_available","prices"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/plans/{plan}/access-template":{"post":{"operationId":"accessTemplate.linkPlan","summary":"#4 \u2014 link (or unlink) an access template to a plan. Both the plan and the\ntemplate are resolved within the brand scope so cross-brand ids 404; passing\na null access_template_id clears the link. Lives here (not on PlanController)\nso plan management is untouched","tags":["Memberships & Contracts"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"access_template_id":{"type":["string","null"]}},"required":["access_template_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"access_template_id":{"type":"string"}},"required":["id","access_template_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/group-memberships":{"get":{"operationId":"groupMembership.index","summary":"List all of the brand's group memberships with their seats","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"group_memberships":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"primary_member_id":{"type":["string","null"]},"membership_plan_id":{"type":"string"},"max_members":{"type":"integer"},"member_count":{"type":"integer","minimum":0},"is_active":{"type":"boolean"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"group_membership_id":{"type":"string"},"member_id":{"type":"string"},"is_primary":{"type":"boolean"},"joined_at":{"type":"string"}},"required":["id","group_membership_id","member_id","is_primary","joined_at"]}}},"required":["id","name","primary_member_id","membership_plan_id","max_members","member_count","is_active","members"]}}},"required":["group_memberships"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"groupMembership.store","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"membership_plan_id":{"type":"string"},"primary_member_id":{"type":["string","null"]},"max_members":{"type":"integer","minimum":1,"maximum":50},"is_active":{"type":"boolean"}},"required":["name","membership_plan_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"primary_member_id":{"type":["string","null"]},"membership_plan_id":{"type":"string"},"max_members":{"type":"integer"},"member_count":{"type":"integer","minimum":0},"is_active":{"type":"boolean"},"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"group_membership_id":{"type":"string"},"member_id":{"type":"string"},"is_primary":{"type":"boolean"},"joined_at":{"type":"string"}},"required":["id","group_membership_id","member_id","is_primary","joined_at"]}}},"required":["id","name","primary_member_id","membership_plan_id","max_members","member_count","is_active","members"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/group-memberships/{group}/members":{"get":{"operationId":"groupMembership.members","summary":"List the seats of a single group","tags":["Memberships & Contracts"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"string"}},"required":["members"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"groupMembership.addMember","summary":"Add a member to a group (422 when the group is already at max_members)","tags":["Memberships & Contracts"],"parameters":[{"name":"group","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"is_primary":{"type":"boolean"}},"required":["member_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"group_membership_id":{"type":"string"},"member_id":{"type":"string"},"is_primary":{"type":"boolean"},"joined_at":{"type":"string"}},"required":["id","group_membership_id","member_id","is_primary","joined_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/catalogue/compare":{"get":{"operationId":"catalogue.compare","summary":"#47 Plan Comparison \u2014 normalized side-by-side of up to 4 requested plans.\nGET /catalogue/compare?plan_ids=a,b,c (permission:members.view). For each plan:\nidentity, family/billing/duration, channels, today's effective price (via the real\nresolver), and derived feature flags it carries. Unknown ids are skipped silently","tags":["Memberships & Contracts"],"parameters":[{"name":"plan_ids","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"family":{"type":"string"},"billing_mode":{"type":"string"},"duration_months":{"type":["integer","null"]},"channels":{"type":"array","items":{}},"price_minor":{"type":"null"},"currency":{"type":"string","const":"SAR"},"features":{"type":"object","properties":{"recurring_billing":{"type":"boolean"},"gender_restricted":{"type":"boolean"},"multi_channel":{"type":"boolean"},"pos":{"type":"boolean"},"web":{"type":"boolean"},"mobile":{"type":"boolean"}},"required":["recurring_billing","gender_restricted","multi_channel","pos","web","mobile"]}},"required":["id","code","name","name_ar","family","billing_mode","duration_months","channels","price_minor","currency","features"]}}},"required":["plans"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/fees":{"get":{"operationId":"fee.index","summary":"List all of the brand's fees (active and inactive)","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"fees":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"fee_type":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"trigger":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","code","name","name_ar","fee_type","membership_plan_id","plan_name","amount_minor","currency","trigger","is_active"]}}},"required":["fees"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"fee.store","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"fee_type":{"$ref":"#/components/schemas/FeeType"},"membership_plan_id":{"type":["string","null"],"description":"Optional: bind the fee to one plan (null = brand-wide default for every plan)."},"amount_minor":{"type":"integer","minimum":0},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"trigger":{"$ref":"#/components/schemas/FeeTrigger"},"is_active":{"type":"boolean"}},"required":["code","name","fee_type","amount_minor","trigger"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"fee_type":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"trigger":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","code","name","name_ar","fee_type","membership_plan_id","plan_name","amount_minor","currency","trigger","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/fees/{fee}":{"patch":{"operationId":"fee.update","tags":["Memberships & Contracts"],"parameters":[{"name":"fee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"membership_plan_id":{"type":["string","null"]},"amount_minor":{"type":"integer","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3},"trigger":{"$ref":"#/components/schemas/FeeTrigger"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"fee_type":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"trigger":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","code","name","name_ar","fee_type","membership_plan_id","plan_name","amount_minor","currency","trigger","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"fee.destroy","summary":"Delete a fee (brand-scoped). Its amount-change history is removed with it (cascade)","tags":["Memberships & Contracts"],"parameters":[{"name":"fee","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/fees/{fee}/changes":{"get":{"operationId":"fee.changes","summary":"#49: the fee's amount-change history (latest first)","tags":["Memberships & Contracts"],"parameters":[{"name":"fee","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"changes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"old_amount_minor":{"type":"integer"},"new_amount_minor":{"type":"integer"},"changed_at":{"type":"string"}},"required":["id","old_amount_minor","new_amount_minor","changed_at"]}}},"required":["changes"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/renew-policies":{"get":{"operationId":"membershipConfig.indexRenewPolicies","summary":"List all of the brand's renew policies (active and inactive)","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"renew_policies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"auto_renew":{"type":"boolean"},"grace_days":{"type":"integer"},"reminder_days_before":{"type":"integer"},"price_lock":{"type":"boolean"},"is_active":{"type":"boolean"}},"required":["id","name","auto_renew","grace_days","reminder_days_before","price_lock","is_active"]}}},"required":["renew_policies"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"membershipConfig.storeRenewPolicy","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"auto_renew":{"type":"boolean"},"grace_days":{"type":"integer","minimum":0,"maximum":365},"reminder_days_before":{"type":"integer","minimum":0,"maximum":365},"price_lock":{"type":"boolean"},"is_active":{"type":"boolean"}},"required":["name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"auto_renew":{"type":"boolean"},"grace_days":{"type":"integer"},"reminder_days_before":{"type":"integer"},"price_lock":{"type":"boolean"},"is_active":{"type":"boolean"}},"required":["id","name","auto_renew","grace_days","reminder_days_before","price_lock","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/renew-policies/{renewPolicy}":{"patch":{"operationId":"membershipConfig.updateRenewPolicy","tags":["Memberships & Contracts"],"parameters":[{"name":"renewPolicy","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"auto_renew":{"type":"boolean"},"grace_days":{"type":"integer","minimum":0,"maximum":365},"reminder_days_before":{"type":"integer","minimum":0,"maximum":365},"price_lock":{"type":"boolean"},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"auto_renew":{"type":"boolean"},"grace_days":{"type":"integer"},"reminder_days_before":{"type":"integer"},"price_lock":{"type":"boolean"},"is_active":{"type":"boolean"}},"required":["id","name","auto_renew","grace_days","reminder_days_before","price_lock","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/presale-configs":{"get":{"operationId":"membershipConfig.indexPreSaleConfigs","summary":"List all of the brand's pre-sale configs (active and inactive)","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"presale_configs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"requires_deposit":{"type":"boolean"},"deposit_minor":{"type":"integer"},"cooling_off_days":{"type":"integer"},"hold_days":{"type":"integer"},"deduction_minor":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","name","requires_deposit","deposit_minor","cooling_off_days","hold_days","deduction_minor","is_active"]}}},"required":["presale_configs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"membershipConfig.storePreSaleConfig","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"requires_deposit":{"type":"boolean"},"deposit_minor":{"type":"integer","minimum":0},"cooling_off_days":{"type":"integer","minimum":0,"maximum":365},"hold_days":{"type":"integer","minimum":0,"maximum":365},"deduction_minor":{"type":"integer","minimum":0},"is_active":{"type":"boolean"}},"required":["name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"requires_deposit":{"type":"boolean"},"deposit_minor":{"type":"integer"},"cooling_off_days":{"type":"integer"},"hold_days":{"type":"integer"},"deduction_minor":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","name","requires_deposit","deposit_minor","cooling_off_days","hold_days","deduction_minor","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/presale-configs/{presaleConfig}":{"patch":{"operationId":"membershipConfig.updatePreSaleConfig","tags":["Memberships & Contracts"],"parameters":[{"name":"presaleConfig","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"requires_deposit":{"type":"boolean"},"deposit_minor":{"type":"integer","minimum":0},"cooling_off_days":{"type":"integer","minimum":0,"maximum":365},"hold_days":{"type":"integer","minimum":0,"maximum":365},"deduction_minor":{"type":"integer","minimum":0},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"requires_deposit":{"type":"boolean"},"deposit_minor":{"type":"integer"},"cooling_off_days":{"type":"integer"},"hold_days":{"type":"integer"},"deduction_minor":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","name","requires_deposit","deposit_minor","cooling_off_days","hold_days","deduction_minor","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/freeze":{"post":{"operationId":"contractAction.freeze","summary":"POST /contracts/{contract}/freeze \u2014 request a freeze. Free freezes (within the\ncohort quota) activate immediately; paid ones are returned Pending until the fee\nsettles. Back-dated freezes require approved_by (a GM identifier)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"integer","minimum":1,"maximum":365},"starts_on":{"type":["string","null"],"format":"date-time","description":"Back-dated freezes are not allowed here \u2014 the start date must be today or later. (The\napproval-gated /freeze/{contract}/backdated route remains for exceptional, audited cases.)"},"approved_by":{"type":["string","null"],"maxLength":64},"reason_code":{"type":["string","null"],"maxLength":64},"waive_fee":{"type":["boolean","null"],"description":"A paid freeze (over the free quota): staff collect the fee at the desk and it takes effect\nimmediately (no approval). `waive_fee` applies it free for special cases."}},"required":["days"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/cancel":{"post":{"operationId":"contractAction.cancel","summary":"POST /contracts/{contract}/cancel \u2014 cancel a contract. A cooling-off refund\n(first membership, within 24h, non-penalty) is computed by the service and paid\nto the member's wallet by finance. Penalty cancellations need approved_by","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason_code":{"type":"string","maxLength":64},"penalty":{"type":["boolean","null"]},"approved_by":{"type":["string","null"],"maxLength":64}},"required":["reason_code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"paid_days_remaining":{"type":"integer"},"cancelled_at":{"type":"string"},"cancellation_reason":{"type":["string","null"]},"closed_at":{"type":"string"},"fraud":{"type":"boolean"},"fraud_reason_code":{"type":["string","null"]},"blocked_at":{"type":"string"},"blocked_reason":{"type":["string","null"]}},"required":["id","member_id","state","contract_type","amount_minor","currency","paid_days_remaining","cancelled_at","cancellation_reason","closed_at","fraud","fraud_reason_code","blocked_at","blocked_reason"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/rewrite":{"post":{"operationId":"contractAction.rewrite","summary":"POST /contracts/{contract}/rewrite \u2014 transfer an upfront contract to another\nperson (once per lifetime). The old contract is voided and a new active one is\ncreated for the new member; paid days carry over","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"new_member_id":{"type":"string","maxLength":64},"payment_method":{"type":"string","description":"How the transfer fee is collected. 'counter' (default) = staff take it front-desk and we\nonly issue the receipt (legacy). The other three route the fee through a real charge and\nthe transfer only completes once it's paid.","enum":["counter","pos","saved_card","online"]},"tender":{"type":"string","description":"POS tender when payment_method=pos.","enum":["cash","card","wallet"]}},"required":["new_member_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/change-plan":{"post":{"operationId":"contractAction.changePlan","summary":"POST /contracts/{contract}/change-plan \u2014 upgrade/downgrade an active contract\nto another plan (RFP 01 #34). Remaining paid days are prorated to the new\nplan's daily rate. The plan (and optional explicit price point) are resolved\nbrand-scoped inside the action","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan_id":{"type":"string","maxLength":64},"price_point_id":{"type":["string","null"],"maxLength":64}},"required":["plan_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"plan_id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"price_minor":{"type":"integer"},"currency":{"type":"string"},"paid_days_remaining":{"type":"integer"}},"required":["id","plan_id","state","contract_type","price_minor","currency","paid_days_remaining"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/reactivate":{"post":{"operationId":"contractAction.reactivate","summary":"POST /contracts/{contract}/reactivate \u2014 restore a recently lapsed contract\n(RFP 01 #36). Within the grace window the original contract returns to Active\n(200); beyond it a fresh sale is required, signalled as 422 with\nrequires_new_contract=true (#37)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"paid_days_remaining":{"type":"integer"},"cancelled_at":{"type":"string"},"cancellation_reason":{"type":["string","null"]},"closed_at":{"type":"string"},"fraud":{"type":"boolean"},"fraud_reason_code":{"type":["string","null"]},"blocked_at":{"type":"string"},"blocked_reason":{"type":["string","null"]}},"required":["id","member_id","state","contract_type","amount_minor","currency","paid_days_remaining","cancelled_at","cancellation_reason","closed_at","fraud","fraud_reason_code","blocked_at","blocked_reason"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"requires_new_contract":{"type":"boolean"}},"required":["message","requires_new_contract"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/withdraw-cancellation":{"post":{"operationId":"contractAction.withdrawCancellation","summary":"POST /contracts/{contract}/withdraw-cancellation \u2014 un-cancel a pending cancellation during the\nnotice window, restoring the contract to Active (RFP 01 #12). (members.manage)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"paid_days_remaining":{"type":"integer"},"cancelled_at":{"type":"string"},"cancellation_reason":{"type":["string","null"]},"closed_at":{"type":"string"},"fraud":{"type":"boolean"},"fraud_reason_code":{"type":["string","null"]},"blocked_at":{"type":"string"},"blocked_reason":{"type":["string","null"]}},"required":["id","member_id","state","contract_type","amount_minor","currency","paid_days_remaining","cancelled_at","cancellation_reason","closed_at","fraud","fraud_reason_code","blocked_at","blocked_reason"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/block":{"post":{"operationId":"contractAction.block","summary":"POST /contracts/{contract}/block \u2014 block a membership for misconduct/debt. Suspends gate access\n(facility listener) and is reversible via unblock. Needs a reason code. (members.manage)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason_code":{"type":"string","maxLength":120}},"required":["reason_code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"paid_days_remaining":{"type":"integer"},"cancelled_at":{"type":"string"},"cancellation_reason":{"type":["string","null"]},"closed_at":{"type":"string"},"fraud":{"type":"boolean"},"fraud_reason_code":{"type":["string","null"]},"blocked_at":{"type":"string"},"blocked_reason":{"type":["string","null"]}},"required":["id","member_id","state","contract_type","amount_minor","currency","paid_days_remaining","cancelled_at","cancellation_reason","closed_at","fraud","fraud_reason_code","blocked_at","blocked_reason"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/unblock":{"post":{"operationId":"contractAction.unblock","summary":"POST /contracts/{contract}/unblock \u2014 lift a block, restoring Active + gate access. (members.manage)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"paid_days_remaining":{"type":"integer"},"cancelled_at":{"type":"string"},"cancellation_reason":{"type":["string","null"]},"closed_at":{"type":"string"},"fraud":{"type":"boolean"},"fraud_reason_code":{"type":["string","null"]},"blocked_at":{"type":"string"},"blocked_reason":{"type":["string","null"]}},"required":["id","member_id","state","contract_type","amount_minor","currency","paid_days_remaining","cancelled_at","cancellation_reason","closed_at","fraud","fraud_reason_code","blocked_at","blocked_reason"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/freeze/policy/{contract}":{"get":{"operationId":"freeze.policy","summary":"GET /freeze/policy/{contract} \u2014 RFP-01 #53. Return the cohort-grandfathered\nfreeze policy resolved for the contract by its sale/start date","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"contract_id":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"cohort_effective_from":{"type":"string"},"free_days":{"type":"integer"},"max_requests":{"type":"integer"},"min_duration_days":{"type":"integer"},"paid_fee_minor":{"type":"integer"},"complimentary_days":{"type":["integer","null"]},"currency":{"type":"string"}},"required":["id","contract_id","membership_plan_id","cohort_effective_from","free_days","max_requests","min_duration_days","paid_fee_minor","complimentary_days","currency"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/freeze/{contract}/backdated":{"post":{"operationId":"freeze.requestBackdated","summary":"POST /freeze/{contract}/backdated \u2014 RFP-01 #30. A back-dated freeze cannot be\napplied unilaterally: it opens a maker-checker Approval (Module 05) and parks a\nPending FreezeRequest. A checker drives the approval to a terminal state; only\nthen is the freeze applied (out of scope here \u2014 emission/approval only)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"start_date":{"type":"string","format":"date-time"},"days":{"type":"integer","minimum":1,"maximum":365},"reason_code":{"type":"string","maxLength":64}},"required":["start_date","days","reason_code"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"pending_approval"},"approval_id":{"type":"string"},"freeze_request_id":{"type":"string"}},"required":["status","approval_id","freeze_request_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/freeze/{contract}/unfreeze":{"post":{"operationId":"freeze.unfreeze","summary":"POST /freeze/{contract}/unfreeze \u2014 RFP-01 #31. Lift the contract's active freeze\nearly: restore the days, shorten the term, and EMIT a ContractUnfrozen domain\nevent into the transactional outbox. External webhook DELIVERY is deferred to\nModule 21 Integrations \u2014 this only emits + records","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"contract_id":{"type":"string"},"status":{"type":"string"},"days_restored":{"type":"integer"}},"required":["id","contract_id","status","days_restored"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]},{"type":"object","properties":{"message":{"type":"string","const":"No active freeze to lift."}},"required":["message"]}]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/freeze-policies":{"get":{"operationId":"freezePolicy.index","summary":"List this brand's freeze policies (brand default first, then per-plan), with the plan name","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"policies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{"type":"string"},"cohort_effective_from":{"type":"string","description":"null = brand-wide default"},"free_days":{"type":"integer"},"max_requests":{"type":"integer"},"min_duration_days":{"type":"integer"},"paid_fee_minor":{"type":"integer"},"complimentary_days":{"type":["integer","null"]},"currency":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","membership_plan_id","plan_name","cohort_effective_from","free_days","max_requests","min_duration_days","paid_fee_minor","complimentary_days","currency","is_active"]}},"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["policies","plans"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"freezePolicy.store","tags":["Memberships & Contracts"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{"type":"string"},"cohort_effective_from":{"type":"string","description":"null = brand-wide default"},"free_days":{"type":"integer"},"max_requests":{"type":"integer"},"min_duration_days":{"type":"integer"},"paid_fee_minor":{"type":"integer"},"complimentary_days":{"type":["integer","null"]},"currency":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","membership_plan_id","plan_name","cohort_effective_from","free_days","max_requests","min_duration_days","paid_fee_minor","complimentary_days","currency","is_active"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/freeze-policies/{policy}":{"patch":{"operationId":"freezePolicy.update","tags":["Memberships & Contracts"],"parameters":[{"name":"policy","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{"type":"string"},"cohort_effective_from":{"type":"string","description":"null = brand-wide default"},"free_days":{"type":"integer"},"max_requests":{"type":"integer"},"min_duration_days":{"type":"integer"},"paid_fee_minor":{"type":"integer"},"complimentary_days":{"type":["integer","null"]},"currency":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","membership_plan_id","plan_name","cohort_effective_from","free_days","max_requests","min_duration_days","paid_fee_minor","complimentary_days","currency","is_active"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"freezePolicy.destroy","tags":["Memberships & Contracts"],"parameters":[{"name":"policy","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/rewrite-policies":{"get":{"operationId":"rewritePolicy.index","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"policies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{"type":"string"},"cohort_effective_from":{"type":"string","description":"null = brand-wide default"},"enabled":{"type":"boolean"},"max_transfers":{"type":"integer"},"min_days_remaining":{"type":"integer"},"allow_installment":{"type":"boolean"},"require_gender_match":{"type":"boolean"},"is_active":{"type":"boolean"},"fee_mode":{"type":"string"},"fee_fixed_minor":{"type":"integer"},"fee_percent":{"type":"number"}},"required":["id","membership_plan_id","plan_name","cohort_effective_from","enabled","max_transfers","min_days_remaining","allow_installment","require_gender_match","is_active","fee_mode","fee_fixed_minor","fee_percent"]}},"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}}},"required":["policies","plans"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"rewritePolicy.store","tags":["Memberships & Contracts"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{"type":"string"},"cohort_effective_from":{"type":"string","description":"null = brand-wide default"},"enabled":{"type":"boolean"},"max_transfers":{"type":"integer"},"min_days_remaining":{"type":"integer"},"allow_installment":{"type":"boolean"},"require_gender_match":{"type":"boolean"},"is_active":{"type":"boolean"},"fee_mode":{"type":"string"},"fee_fixed_minor":{"type":"integer"},"fee_percent":{"type":"number"}},"required":["id","membership_plan_id","plan_name","cohort_effective_from","enabled","max_transfers","min_days_remaining","allow_installment","require_gender_match","is_active","fee_mode","fee_fixed_minor","fee_percent"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/rewrite-policies/{policy}":{"patch":{"operationId":"rewritePolicy.update","tags":["Memberships & Contracts"],"parameters":[{"name":"policy","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"membership_plan_id":{"type":["string","null"]},"plan_name":{"type":"string"},"cohort_effective_from":{"type":"string","description":"null = brand-wide default"},"enabled":{"type":"boolean"},"max_transfers":{"type":"integer"},"min_days_remaining":{"type":"integer"},"allow_installment":{"type":"boolean"},"require_gender_match":{"type":"boolean"},"is_active":{"type":"boolean"},"fee_mode":{"type":"string"},"fee_fixed_minor":{"type":"integer"},"fee_percent":{"type":"number"}},"required":["id","membership_plan_id","plan_name","cohort_effective_from","enabled","max_transfers","min_days_remaining","allow_installment","require_gender_match","is_active","fee_mode","fee_fixed_minor","fee_percent"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"rewritePolicy.destroy","tags":["Memberships & Contracts"],"parameters":[{"name":"policy","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/signatures":{"post":{"operationId":"signature.store","summary":"Capture a signature for a member document (brand-scoped)","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"contract_id":{"type":["string","null"]},"document":{"type":"string","maxLength":255},"signature":{"type":"string"},"signer_name":{"type":["string","null"],"maxLength":255}},"required":["member_id","document","signature"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"document":{"type":"string"},"signed_at":{"type":"string"}},"required":["id","document","signed_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/club-closure-refund":{"post":{"operationId":"contractAction.clubClosureRefund","summary":"POST /contracts/{contract}/club-closure-refund \u2014 permanent club closure (RFP 01\n#51). Pro-rata refund of the remaining paid days to the member wallet (reusing\nthe existing refund seam) and close the contract. (members.manage)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"paid_days_remaining":{"type":"integer"},"cancelled_at":{"type":"string"},"cancellation_reason":{"type":["string","null"]},"closed_at":{"type":"string"},"fraud":{"type":"boolean"},"fraud_reason_code":{"type":["string","null"]},"blocked_at":{"type":"string"},"blocked_reason":{"type":["string","null"]}},"required":["id","member_id","state","contract_type","amount_minor","currency","paid_days_remaining","cancelled_at","cancellation_reason","closed_at","fraud","fraud_reason_code","blocked_at","blocked_reason"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/cancel-fraud":{"post":{"operationId":"contractAction.cancelFraud","summary":"POST /contracts/{contract}/cancel-fraud \u2014 cancel a contract for fraud WITHOUT any\nrefund, recording the fraud flag + reason code (RFP 01 #52). (members.manage)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason_code":{"type":"string","maxLength":64}},"required":["reason_code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"state":{"type":"string"},"contract_type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"paid_days_remaining":{"type":"integer"},"cancelled_at":{"type":"string"},"cancellation_reason":{"type":["string","null"]},"closed_at":{"type":"string"},"fraud":{"type":"boolean"},"fraud_reason_code":{"type":["string","null"]},"blocked_at":{"type":"string"},"blocked_reason":{"type":["string","null"]}},"required":["id","member_id","state","contract_type","amount_minor","currency","paid_days_remaining","cancelled_at","cancellation_reason","closed_at","fraud","fraud_reason_code","blocked_at","blocked_reason"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/memberships":{"get":{"operationId":"membershipList.index","tags":["Memberships & Contracts"],"parameters":[{"name":"state","in":"query","schema":{"type":["string","null"],"enum":["all","active","expiring","grace","frozen","pending_cancellation","blocked","expired","cancelled"]}},{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"club_id","in":"query","schema":{"type":["string","null"]}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"memberships":{"type":"array","items":{"type":"object","properties":{"contract_id":{"type":"string"},"member_id":{"type":"string"},"member_name":{"type":["string","null"]},"plan_name":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":["string","null"]},"state":{"type":"string"},"activation_date":{"type":"string"},"expiry_date":{"type":"string"},"days_remaining":{"type":["integer","null"]},"in_grace":{"type":"string"},"grace_until":{"type":"string"}},"required":["contract_id","member_id","member_name","plan_name","club_id","club_name","state","activation_date","expiry_date","days_remaining","in_grace","grace_until"]}},"counts":{"type":"string"},"total":{"type":"string"},"page":{"type":"integer"},"per_page":{"type":"integer","const":20}},"required":["memberships","counts","total","page","per_page"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/memberships/run-expiry":{"post":{"operationId":"membershipList.runExpiry","summary":"POST /memberships/run-expiry \u2014 expire lapsed paid-in-full memberships for this brand (demo trigger)","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"expired":{"type":"integer","minimum":0}},"required":["expired"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/allowed-actions":{"get":{"operationId":"contractAction.allowedActions","summary":"GET /contracts/{contract}/allowed-actions \u2014 read the lifecycle actions permitted\non this contract given its plan family and any complimentary months (RFP 01\n#35/#55). Also surfaces the active rewrite fee (#56). Read-only \u2014 does not change\nany state or the existing action endpoints' behaviour. (members.view)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"allowed":{"type":"object","properties":{"freeze":{"type":"string","description":"Freeze only an active membership; unfreeze only a frozen one \u2014 so staff see the right action."},"unfreeze":{"type":"boolean"},"cancel":{"type":"boolean"},"rewrite":{"type":"boolean"},"change_plan":{"type":"boolean"},"withdraw_cancellation":{"type":"boolean"},"reactivate":{"type":"boolean"},"block":{"type":"boolean"},"unblock":{"type":"boolean"}},"required":["freeze","unfreeze","cancel","rewrite","change_plan","withdraw_cancellation","reactivate","block","unblock"]},"complimentary_months":{"type":"integer"},"rewrite_fee_minor":{"type":"integer"},"rewrite_fee_mode":{"type":"string"},"currency":{"type":"string"}},"required":["allowed","complimentary_months","rewrite_fee_minor","rewrite_fee_mode","currency"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/contracts/{contract}/missed-renewal-liability":{"get":{"operationId":"contractAction.missedRenewalLiability","summary":"GET /contracts/{contract}/missed-renewal-liability \u2014 the one-month pro-rated\nliability owed when a contract has lapsed past expiry without renewal (RFP 01\n#50). 0 when not yet due. Read-only. (members.view)","tags":["Memberships & Contracts"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contract_id":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"}},"required":["contract_id","amount_minor","currency"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/approvals":{"get":{"operationId":"approval.index","summary":"Paginated list of the brand's approval requests, newest first, with status / process / search filters","tags":["Memberships & Contracts"],"parameters":[{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/ApprovalStatus"}},{"name":"process","in":"query","schema":{"type":["string","null"],"maxLength":60}},{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":120}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"],"minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"approvals":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"process":{"type":"string"},"subject_type":{"type":"string"},"subject_id":{"type":"string"},"subject_label":{},"requested_by":{"type":"string"},"status":{"type":"string"},"stage_count":{"type":"integer","description":"Multi-level progress: which stage of how many, and the level the next approval needs."},"current_stage":{"type":"integer"},"current_stage_permission":{"type":["string","null"]},"decisions":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"integer"},"status":{"type":"string"},"decided_by":{"type":"string"},"reason":{"type":["string","null"]},"decided_at":{"type":"string"}},"required":["stage","status","decided_by","reason","decided_at"]}},"decided_by":{"type":["string","null"]},"decided_at":{"type":"string"},"reason":{"type":["string","null"]},"payload":{"type":["array","null"],"items":{}},"created_at":{"type":"string"}},"required":["id","process","subject_type","subject_id","subject_label","requested_by","status","stage_count","current_stage","current_stage_permission","decisions","decided_by","decided_at","reason","payload","created_at"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"last_page":{"type":"integer"}},"required":["page","per_page","total","last_page"]},"processes":{"type":"array","description":"The selectable processes, for the filter dropdown.","items":{}}},"required":["approvals","meta","processes"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"approval.store","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"process":{"type":"string","maxLength":60},"subject_type":{"type":"string","maxLength":120},"subject_id":{"type":"string","maxLength":40},"requested_by":{"type":"string","maxLength":120},"payload":{"type":["array","null"],"items":{"type":"string"}}},"required":["process","subject_type","subject_id","requested_by"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"process":{"type":"string"},"subject_type":{"type":"string"},"subject_id":{"type":"string"},"subject_label":{},"requested_by":{"type":"string"},"status":{"type":"string"},"stage_count":{"type":"integer","description":"Multi-level progress: which stage of how many, and the level the next approval needs."},"current_stage":{"type":"integer"},"current_stage_permission":{"type":["string","null"]},"decisions":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"integer"},"status":{"type":"string"},"decided_by":{"type":"string"},"reason":{"type":["string","null"]},"decided_at":{"type":"string"}},"required":["stage","status","decided_by","reason","decided_at"]}},"decided_by":{"type":["string","null"]},"decided_at":{"type":"string"},"reason":{"type":["string","null"]},"payload":{"type":["array","null"],"items":{}},"created_at":{"type":"string"}},"required":["id","process","subject_type","subject_id","subject_label","requested_by","status","stage_count","current_stage","current_stage_permission","decisions","decided_by","decided_at","reason","payload","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/approvals/{approval}/approve":{"post":{"operationId":"approval.approve","tags":["Memberships & Contracts"],"parameters":[{"name":"approval","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decided_by":{"type":"string","maxLength":120}},"required":["decided_by"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"process":{"type":"string"},"subject_type":{"type":"string"},"subject_id":{"type":"string"},"subject_label":{},"requested_by":{"type":"string"},"status":{"type":"string"},"stage_count":{"type":"integer","description":"Multi-level progress: which stage of how many, and the level the next approval needs."},"current_stage":{"type":"integer"},"current_stage_permission":{"type":["string","null"]},"decisions":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"integer"},"status":{"type":"string"},"decided_by":{"type":"string"},"reason":{"type":["string","null"]},"decided_at":{"type":"string"}},"required":["stage","status","decided_by","reason","decided_at"]}},"decided_by":{"type":["string","null"]},"decided_at":{"type":"string"},"reason":{"type":["string","null"]},"payload":{"type":["array","null"],"items":{}},"created_at":{"type":"string"}},"required":["id","process","subject_type","subject_id","subject_label","requested_by","status","stage_count","current_stage","current_stage_permission","decisions","decided_by","decided_at","reason","payload","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/approvals/{approval}/reject":{"post":{"operationId":"approval.reject","tags":["Memberships & Contracts"],"parameters":[{"name":"approval","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"decided_by":{"type":"string","maxLength":120},"reason":{"type":["string","null"],"maxLength":500}},"required":["decided_by"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"process":{"type":"string"},"subject_type":{"type":"string"},"subject_id":{"type":"string"},"subject_label":{},"requested_by":{"type":"string"},"status":{"type":"string"},"stage_count":{"type":"integer","description":"Multi-level progress: which stage of how many, and the level the next approval needs."},"current_stage":{"type":"integer"},"current_stage_permission":{"type":["string","null"]},"decisions":{"type":"array","items":{"type":"object","properties":{"stage":{"type":"integer"},"status":{"type":"string"},"decided_by":{"type":"string"},"reason":{"type":["string","null"]},"decided_at":{"type":"string"}},"required":["stage","status","decided_by","reason","decided_at"]}},"decided_by":{"type":["string","null"]},"decided_at":{"type":"string"},"reason":{"type":["string","null"]},"payload":{"type":["array","null"],"items":{}},"created_at":{"type":"string"}},"required":["id","process","subject_type","subject_id","subject_label","requested_by","status","stage_count","current_stage","current_stage_permission","decisions","decided_by","decided_at","reason","payload","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addons":{"get":{"operationId":"addOn.index","summary":"List the brand's active add-ons (catalogue for the POS / sale form). Supports\nRFP-02 filters: ?club_id= keeps only add-ons applicable to that club (#8) and\n?available=1 keeps only add-ons sellable today (#7). Each row carries its\navailability window, club applicability, and a computed is_available flag","tags":["Memberships & Contracts"],"parameters":[{"name":"club_id","in":"query","schema":{"type":"string"}},{"name":"available","in":"query","schema":{"type":"boolean"}},{"name":"status","in":"query","description":"Management filters (config surface). Default (no status) = active only, so POS/enroll are unchanged.","schema":{"type":"string","enum":["all","active","inactive"]}},{"name":"kind","in":"query","schema":{"$ref":"#/components/schemas/AddOnKind"}},{"name":"add_on_type_id","in":"query","schema":{"type":"string"}},{"name":"q","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"add_ons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"kind":{"type":"string"},"gender":{"type":"string"},"credits":{"type":["integer","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"validity_days":{"type":["integer","null"]},"is_active":{"type":"boolean"},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"club_ids":{"type":["array","null"],"items":{}},"add_on_type_id":{"type":["string","null"]},"max_per_day":{"type":["integer","null"]},"min_hours_between":{"type":["integer","null"]},"setup_fee_minor":{"type":["integer","null"]},"duration_minutes":{"type":["integer","null"]},"buffer_minutes":{"type":["integer","null"]},"is_available":{"type":"boolean"}},"required":["id","code","name","name_ar","image_url","kind","gender","credits","price_minor","currency","validity_days","is_active","valid_from","valid_to","club_ids","add_on_type_id","max_per_day","min_hours_between","setup_fee_minor","duration_minutes","buffer_minutes","is_available"]}}},"required":["add_ons"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"addOn.store","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"image_url":{"type":["string","null"],"maxLength":1000},"kind":{"$ref":"#/components/schemas/AddOnKind"},"gender":{"$ref":"#/components/schemas/Gender"},"credits":{"type":["integer","null"],"minimum":1},"price_minor":{"type":"integer","minimum":0},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"validity_days":{"type":["integer","null"],"minimum":1},"is_active":{"type":"boolean"},"valid_from":{"type":["string","null"],"format":"date-time"},"valid_to":{"type":["string","null"],"format":"date-time"},"add_on_type_id":{"type":["string","null"]},"max_per_day":{"type":["integer","null"],"description":"RFP-02 #5 usage rules (null = unlimited).","minimum":1},"min_hours_between":{"type":["integer","null"],"minimum":1},"setup_fee_minor":{"type":["integer","null"],"description":"RFP-02 #26 per-add-on fee + #27 session duration (all optional).","minimum":0},"duration_minutes":{"type":["integer","null"],"minimum":1},"buffer_minutes":{"type":["integer","null"],"minimum":0},"club_ids":{"type":["array","null"],"items":{"type":"string"}}},"required":["code","name","kind","price_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"kind":{"type":"string"},"gender":{"type":"string"},"credits":{"type":["integer","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"validity_days":{"type":["integer","null"]},"is_active":{"type":"boolean"},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"club_ids":{"type":["array","null"],"items":{}},"add_on_type_id":{"type":["string","null"]},"max_per_day":{"type":["integer","null"]},"min_hours_between":{"type":["integer","null"]},"setup_fee_minor":{"type":["integer","null"]},"duration_minutes":{"type":["integer","null"]},"buffer_minutes":{"type":["integer","null"]},"is_available":{"type":"boolean"}},"required":["id","code","name","name_ar","image_url","kind","gender","credits","price_minor","currency","validity_days","is_active","valid_from","valid_to","club_ids","add_on_type_id","max_per_day","min_hours_between","setup_fee_minor","duration_minutes","buffer_minutes","is_available"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addons/upload":{"post":{"operationId":"addOn.upload","summary":"Upload an add-on image; returns the public URL to store on the add-on (config.manage)","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","maxLength":25600}},"required":["file"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addons/{addon}":{"get":{"operationId":"addOn.show","summary":"RFP-02 #3 \u2014 a single add-on for the config view/edit pages (members.view)","tags":["Memberships & Contracts"],"parameters":[{"name":"addon","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"kind":{"type":"string"},"gender":{"type":"string"},"credits":{"type":["integer","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"validity_days":{"type":["integer","null"]},"is_active":{"type":"boolean"},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"club_ids":{"type":["array","null"],"items":{}},"add_on_type_id":{"type":["string","null"]},"max_per_day":{"type":["integer","null"]},"min_hours_between":{"type":["integer","null"]},"setup_fee_minor":{"type":["integer","null"]},"duration_minutes":{"type":["integer","null"]},"buffer_minutes":{"type":["integer","null"]},"is_available":{"type":"boolean"}},"required":["id","code","name","name_ar","image_url","kind","gender","credits","price_minor","currency","validity_days","is_active","valid_from","valid_to","club_ids","add_on_type_id","max_per_day","min_hours_between","setup_fee_minor","duration_minutes","buffer_minutes","is_available"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"addOn.update","summary":"RFP-02 #3 \u2014 update an add-on (config.manage). All fields optional; only those\npresent are written. Resolve inside the action so the brand global scope applies\n(no route-model binding). Deactivating is just is_active=false through this method","tags":["Memberships & Contracts"],"parameters":[{"name":"addon","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"image_url":{"type":["string","null"],"maxLength":1000},"gender":{"$ref":"#/components/schemas/Gender"},"credits":{"type":["integer","null"],"minimum":1},"price_minor":{"type":"integer","minimum":0},"validity_days":{"type":["integer","null"],"minimum":1},"valid_from":{"type":["string","null"],"format":"date-time"},"valid_to":{"type":["string","null"],"format":"date-time"},"add_on_type_id":{"type":["string","null"]},"is_active":{"type":"boolean"},"max_per_day":{"type":["integer","null"],"description":"RFP-02 #5 usage rules + #26 per-add-on fee + #27 session duration.","minimum":1},"min_hours_between":{"type":["integer","null"],"minimum":1},"setup_fee_minor":{"type":["integer","null"],"minimum":0},"duration_minutes":{"type":["integer","null"],"minimum":1},"buffer_minutes":{"type":["integer","null"],"minimum":0},"club_ids":{"type":["array","null"],"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"kind":{"type":"string"},"gender":{"type":"string"},"credits":{"type":["integer","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"validity_days":{"type":["integer","null"]},"is_active":{"type":"boolean"},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"club_ids":{"type":["array","null"],"items":{}},"add_on_type_id":{"type":["string","null"]},"max_per_day":{"type":["integer","null"]},"min_hours_between":{"type":["integer","null"]},"setup_fee_minor":{"type":["integer","null"]},"duration_minutes":{"type":["integer","null"]},"buffer_minutes":{"type":["integer","null"]},"is_available":{"type":"boolean"}},"required":["id","code","name","name_ar","image_url","kind","gender","credits","price_minor","currency","validity_days","is_active","valid_from","valid_to","club_ids","add_on_type_id","max_per_day","min_hours_between","setup_fee_minor","duration_minutes","buffer_minutes","is_available"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/member-addons/{grant}/consume":{"post":{"operationId":"addOn.consume","summary":"Consume one (or more) session credits from a grant","tags":["Memberships & Contracts"],"parameters":[{"name":"grant","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"qty":{"type":"integer","minimum":1}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"add_on_id":{"type":"string"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]},"credits_total":{"type":["integer","null"]},"credits_used":{"type":"integer"},"remaining_credits":{"type":["integer","null"]},"expires_at":{"type":"string"},"status":{"type":"string"}},"required":["id","member_id","add_on_id","add_on","credits_total","credits_used","remaining_credits","expires_at","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addons/{addon}/copy":{"post":{"operationId":"addOn.copy","summary":"RFP-02 #3 \u2014 deep-clone an add-on (config.manage) into a new, inactive add-on under\na brand-unique code. Lets staff duplicate a template add-on and tweak before sale","tags":["Memberships & Contracts"],"parameters":[{"name":"addon","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":["string","null"],"maxLength":120}},"required":["code"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"kind":{"type":"string"},"gender":{"type":"string"},"credits":{"type":["integer","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"validity_days":{"type":["integer","null"]},"is_active":{"type":"boolean"},"valid_from":{"type":"string"},"valid_to":{"type":"string"},"club_ids":{"type":["array","null"],"items":{}},"add_on_type_id":{"type":["string","null"]},"max_per_day":{"type":["integer","null"]},"min_hours_between":{"type":["integer","null"]},"setup_fee_minor":{"type":["integer","null"]},"duration_minutes":{"type":["integer","null"]},"buffer_minutes":{"type":["integer","null"]},"is_available":{"type":"boolean"}},"required":["id","code","name","name_ar","image_url","kind","gender","credits","price_minor","currency","validity_days","is_active","valid_from","valid_to","club_ids","add_on_type_id","max_per_day","min_hours_between","setup_fee_minor","duration_minutes","buffer_minutes","is_available"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/member-addons/{grant}/freeze":{"post":{"operationId":"addOnGrant.freeze","summary":"POST /member-addons/{grant}/freeze \u2014 RFP-02 #20. Freeze a single grant\nindependently of any contract; a frozen grant cannot be consumed. Idempotent","tags":["Memberships & Contracts"],"parameters":[{"name":"grant","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason_code":{"type":"string","maxLength":64}},"required":["reason_code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"add_on_id":{"type":"string"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]},"credits_total":{"type":["integer","null"]},"credits_used":{"type":"integer"},"remaining_credits":{"type":["integer","null"]},"expires_at":{"type":"string"},"status":{"type":"string"},"frozen_at":{"type":"string"},"is_frozen":{"type":"boolean"},"transfer_rule":{"type":"string"}},"required":["id","member_id","add_on_id","add_on","credits_total","credits_used","remaining_credits","expires_at","status","frozen_at","is_frozen","transfer_rule"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/member-addons/{grant}/unfreeze":{"post":{"operationId":"addOnGrant.unfreeze","summary":"POST /member-addons/{grant}/unfreeze \u2014 RFP-02 #20. Lift the suspend on a grant.\nStandalone (operator-initiated) unfreeze does NOT extend expiry \u2014 that extension\nis reserved for the contract-cascade path (#18). Idempotent","tags":["Memberships & Contracts"],"parameters":[{"name":"grant","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason_code":{"type":"string","maxLength":64}},"required":["reason_code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"add_on_id":{"type":"string"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]},"credits_total":{"type":["integer","null"]},"credits_used":{"type":"integer"},"remaining_credits":{"type":["integer","null"]},"expires_at":{"type":"string"},"status":{"type":"string"},"frozen_at":{"type":"string"},"is_frozen":{"type":"boolean"},"transfer_rule":{"type":"string"}},"required":["id","member_id","add_on_id","add_on","credits_total","credits_used","remaining_credits","expires_at","status","frozen_at","is_frozen","transfer_rule"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/member-addons/{grant}/adjust":{"post":{"operationId":"addOnGrant.adjust","summary":"POST /member-addons/{grant}/adjust \u2014 RFP-02 #14. Manually restore (+delta) or\ndeduct (-delta) consumed credits with a mandatory reason. A back-dated adjustment\nor one whose magnitude exceeds the threshold opens a maker-checker Approval\n(Module 05) in addition to applying the change, so the audit trail captures it","tags":["Memberships & Contracts"],"parameters":[{"name":"grant","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"delta":{"type":"integer"},"reason_code":{"type":"string","maxLength":64},"effective_date":{"type":"string","format":"date-time"}},"required":["delta","reason_code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"grant":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"add_on_id":{"type":"string"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"kind":{"type":"string"}},"required":["id","code","name","kind"]},"credits_total":{"type":["integer","null"]},"credits_used":{"type":"integer"},"remaining_credits":{"type":["integer","null"]},"expires_at":{"type":"string"},"status":{"type":"string"},"frozen_at":{"type":"string"},"is_frozen":{"type":"boolean"},"transfer_rule":{"type":"string"}},"required":["id","member_id","add_on_id","add_on","credits_total","credits_used","remaining_credits","expires_at","status","frozen_at","is_frozen","transfer_rule"]},"approval_id":{"type":"string"}},"required":["grant","approval_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/renewal-reminders/run":{"post":{"operationId":"memberAlert.runRenewalReminders","summary":"Demo trigger for RFP-12 #6: run the renewal-reminder generator for the current\nbrand only and report how many follow-up tasks were opened. Idempotent \u2014 re-running\nwill not duplicate tasks for contracts that already have an open \"Renewal due\".\nGated by config.manage in routes/api.php","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"}},"required":["created"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addon-types":{"get":{"operationId":"addOnType.index","summary":"#1 \u2014 list the brand's add-on types/groups (active and inactive)","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"addon_types":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","description","is_active"]}}},"required":["addon_types"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"addOnType.store","summary":"#1 \u2014 create an add-on type/group","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"description":{"type":["string","null"],"maxLength":1000},"is_active":{"type":["boolean","null"]}},"required":["name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","description","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addon-types/{type}":{"patch":{"operationId":"addOnType.update","summary":"#1 \u2014 update an add-on type/group","tags":["Memberships & Contracts"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"description":{"type":["string","null"],"maxLength":1000},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","description","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addon-bundles":{"get":{"operationId":"addOnBundle.index","summary":"List the brand's bundles with their items and the undiscounted component value","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"bundles":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"components_value_minor":{"type":"integer"},"items":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"add_on_id":{"type":"string"},"quantity":{"type":"integer"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"price_minor":{"type":"integer"}},"required":["id","code","name","price_minor"]}},"required":["id","add_on_id","quantity","add_on"]}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string"},"add_on_id":{"type":"string"},"quantity":{"type":"integer"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"price_minor":{"type":"integer"}},"required":["id","code","name","price_minor"]}},"required":["id","add_on_id","quantity","add_on"]}}]}},"required":["id","code","name","name_ar","price_minor","currency","is_active","components_value_minor","items"]}}},"required":["bundles"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"addOnBundle.store","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"price_minor":{"type":"integer","minimum":0},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"is_active":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"add_on_id":{"type":"string"},"quantity":{"type":"integer","minimum":1}},"required":["add_on_id"]},"minItems":1}},"required":["code","name","price_minor","items"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"components_value_minor":{"type":"integer"},"items":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"add_on_id":{"type":"string"},"quantity":{"type":"integer"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"price_minor":{"type":"integer"}},"required":["id","code","name","price_minor"]}},"required":["id","add_on_id","quantity","add_on"]}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string"},"add_on_id":{"type":"string"},"quantity":{"type":"integer"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"price_minor":{"type":"integer"}},"required":["id","code","name","price_minor"]}},"required":["id","add_on_id","quantity","add_on"]}}]}},"required":["id","code","name","name_ar","price_minor","currency","is_active","components_value_minor","items"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addon-bundles/{bundle}":{"patch":{"operationId":"addOnBundle.update","summary":"Edit a bundle: toggle active, change pricing/name, or replace its items","tags":["Memberships & Contracts"],"parameters":[{"name":"bundle","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"price_minor":{"type":"integer","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3},"is_active":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"add_on_id":{"type":"string"},"quantity":{"type":"integer","minimum":1}}},"minItems":1}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"components_value_minor":{"type":"integer"},"items":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"add_on_id":{"type":"string"},"quantity":{"type":"integer"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"price_minor":{"type":"integer"}},"required":["id","code","name","price_minor"]}},"required":["id","add_on_id","quantity","add_on"]}},{"type":"object","additionalProperties":{"type":"object","properties":{"id":{"type":"string"},"add_on_id":{"type":"string"},"quantity":{"type":"integer"},"add_on":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"price_minor":{"type":"integer"}},"required":["id","code","name","price_minor"]}},"required":["id","add_on_id","quantity","add_on"]}}]}},"required":["id","code","name","name_ar","price_minor","currency","is_active","components_value_minor","items"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/addons/{addon}/bulk-grant":{"post":{"operationId":"addOnBulk.bulkGrant","summary":"POST /addons/{addon}/bulk-grant \u2014 grant the add-on to each listed member","tags":["Memberships & Contracts"],"parameters":[{"name":"addon","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_ids":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["member_ids"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"granted":{"type":"integer"},"skipped":{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}},"required":["granted","skipped"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/gift-passes":{"get":{"operationId":"giftPass.index","summary":"List the brand's gift passes (?status filter), newest first","tags":["Memberships & Contracts"],"parameters":[{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/GiftPassStatus"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"gift_passes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gifter_member_id":{"type":"string"},"recipient_name":{"type":"string"},"recipient_mobile":{"type":["string","null"]},"membership_plan_id":{"type":"string"},"code":{"type":"string"},"status":{"type":"string"},"expires_at":{"type":"string"},"redeemed_at":{"type":"string"},"redeemed_member_id":{"type":["string","null"]}},"required":["id","gifter_member_id","recipient_name","recipient_mobile","membership_plan_id","code","status","expires_at","redeemed_at","redeemed_member_id"]}}},"required":["gift_passes"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"giftPass.store","summary":"Issue a gift pass (members.manage). The plan must be a day-pass (is_day_pass=true)\nelse 422. Generates a unique-per-brand claim code and returns the issued pass","tags":["Memberships & Contracts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"gifter_member_id":{"type":"string"},"recipient_name":{"type":"string","maxLength":255},"recipient_mobile":{"type":["string","null"],"maxLength":32},"membership_plan_id":{"type":"string"}},"required":["gifter_member_id","recipient_name","membership_plan_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"gifter_member_id":{"type":"string"},"recipient_name":{"type":"string"},"recipient_mobile":{"type":["string","null"]},"membership_plan_id":{"type":"string"},"code":{"type":"string"},"status":{"type":"string"},"expires_at":{"type":"string"},"redeemed_at":{"type":"string"},"redeemed_member_id":{"type":["string","null"]}},"required":["id","gifter_member_id","recipient_name","recipient_mobile","membership_plan_id","code","status","expires_at","redeemed_at","redeemed_member_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/gift-passes/{code}/redeem":{"post":{"operationId":"giftPass.redeem","summary":"Redeem a gift pass by its code (members.manage). 422 if already redeemed or expired\n(a lapsed expiry self-expires here). An optional redeemed_member_id records who claimed it","tags":["Memberships & Contracts"],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"redeemed_member_id":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"gifter_member_id":{"type":"string"},"recipient_name":{"type":"string"},"recipient_mobile":{"type":["string","null"]},"membership_plan_id":{"type":"string"},"code":{"type":"string"},"status":{"type":"string"},"expires_at":{"type":"string"},"redeemed_at":{"type":"string"},"redeemed_member_id":{"type":["string","null"]}},"required":["id","gifter_member_id","recipient_name","recipient_mobile","membership_plan_id","code","status","expires_at","redeemed_at","redeemed_member_id"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/renewals":{"get":{"operationId":"renewal.index","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"renewals":{"type":"string"},"counts":{"type":"object","properties":{"total":{"type":"string"},"expiring":{"type":"string"},"lapsed":{"type":"string"},"follow_up_due":{"type":"string"}},"required":["total","expiring","lapsed","follow_up_due"]},"window":{"type":"object","properties":{"ahead":{"type":["object","null"]},"behind":{"type":["object","null"]}},"required":["ahead","behind"]}},"required":["renewals","counts","window"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/lookups/{type}":{"get":{"operationId":"lookup.index","summary":"GET /manage/lookups/{type} \u2014 every value of this kind (active + inactive), for management","tags":["Memberships & Contracts"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"is_active":{"type":"boolean"},"parent_id":{"type":["string","null"]},"parent_name":{"type":"string"},"country_id":{"type":"string"},"country_name":{"type":"string"}},"required":["id","name","name_ar","is_active","parent_id","parent_name","country_id","country_name"]}}},"required":["items"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"lookup.store","tags":["Memberships & Contracts"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"is_active":{"type":"boolean"},"parent_id":{"type":["string","null"]},"parent_name":{"type":"string"},"country_id":{"type":"string"},"country_name":{"type":"string"}},"required":["id","name","name_ar","is_active","parent_id","parent_name","country_id","country_name"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/lookups/{type}/{lookup}":{"patch":{"operationId":"lookup.update","tags":["Memberships & Contracts"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"lookup","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"is_active":{"type":"boolean"},"parent_id":{"type":["string","null"]},"parent_name":{"type":"string"},"country_id":{"type":"string"},"country_name":{"type":"string"}},"required":["id","name","name_ar","is_active","parent_id","parent_name","country_id","country_name"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"lookup.destroy","tags":["Memberships & Contracts"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"lookup","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/approval-policies":{"get":{"operationId":"approvalPolicy.index","summary":"GET /manage/approval-policies \u2014 every action with its EFFECTIVE policy + the selectable levels","tags":["Memberships & Contracts"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"levels":{"type":"string"},"processes":{"type":"array","items":{"type":"object","properties":{"process":{"type":"string"},"label":{"type":"string"},"stages":{"type":"array","description":"The chain in effect right now (the brand's override if active, else the default).","items":{}},"is_custom":{"type":"boolean"},"is_active":{"type":"boolean"},"default_stages":{"type":"array","items":{}}},"required":["process","label","stages","is_custom","is_active","default_stages"]}}},"required":["levels","processes"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/approval-policies/{process}":{"put":{"operationId":"approvalPolicy.upsert","summary":"PUT /manage/approval-policies/{process} \u2014 set this brand's levels for an action","tags":["Memberships & Contracts"],"parameters":[{"name":"process","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"is_active":{"type":["boolean","null"]},"stages":{"type":"array","items":{"type":"string","enum":["approvals.l1","approvals.l2"]},"minItems":1,"maxItems":2}},"required":["stages"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"process":{"type":"string"},"label":{"type":"string"},"stages":{"type":"array","description":"The chain in effect right now (the brand's override if active, else the default).","items":{}},"is_custom":{"type":"boolean"},"is_active":{"type":"boolean"},"default_stages":{"type":"array","items":{}}},"required":["process","label","stages","is_custom","is_active","default_stages"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"approvalPolicy.destroy","summary":"DELETE /manage/approval-policies/{process} \u2014 revert to the platform default","tags":["Memberships & Contracts"],"parameters":[{"name":"process","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"process":{"type":"string"},"label":{"type":"string"},"stages":{"type":"array","description":"The chain in effect right now (the brand's override if active, else the default).","items":{}},"is_custom":{"type":"boolean"},"is_active":{"type":"boolean"},"default_stages":{"type":"array","items":{}}},"required":["process","label","stages","is_custom","is_active","default_stages"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/health":{"get":{"operationId":"health","tags":["Other"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"service":{"type":"string","const":"memvera"},"status":{"type":"string","const":"ok"},"time":{"type":"object","properties":{"gregorian":{"type":"string"},"hijri":{"type":"string"}},"required":["gregorian","hijri"]},"footprint":{"type":"object","properties":{"brands":{"type":"integer","minimum":0},"clubs":{"type":"integer","minimum":0},"members":{"type":"integer","minimum":0}},"required":["brands","clubs","members"]}},"required":["service","status","time","footprint"]}}}}}}},"/nav/badges":{"get":{"operationId":"navBadge.index","tags":["Other"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"approvals":{"type":"integer"},"validation_pending":{"type":"integer"},"blacklist":{"type":"integer"},"dunning":{"type":"integer"},"corporate":{"type":"integer"},"tickets":{"type":"string","description":"Support page = member tickets (config.manage) + employee helpdesk tickets (tickets.manage)."}},"required":["approvals","validation_pending","blacklist","dunning","corporate","tickets"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/member-activities/{activity}/resolve":{"patch":{"operationId":"memberActivity.resolve","summary":"Close an open activity / complaint (stamps resolved_at)","tags":["Other"],"parameters":[{"name":"activity","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"type":{"type":"string"},"subject":{"type":"string"},"body":{"type":["string","null"]},"status":{"type":"string"},"channel":{"type":["string","null"]},"direction":{"type":["string","null"]},"outcome":{"type":["string","null"]},"duration_sec":{"type":["integer","null"]},"sla_due_at":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"},"image_url":{"type":["string","null"]}},"required":["id","member_id","type","subject","body","status","channel","direction","outcome","duration_sec","sla_due_at","resolved_at","created_at","image_url"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/products":{"get":{"operationId":"catalogue.products","summary":"Active retail products with stock at the given club (0 when no level exists)","tags":["POS & Retail"],"parameters":[{"name":"club_id","in":"query","schema":{"type":["string","null"]}},{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"],"minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sku":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"stock":{"type":"integer"}},"required":["id","sku","name","name_ar","price_minor","currency","stock"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"last_page":{"type":"integer"}},"required":["page","per_page","total","last_page"]}},"required":["products","meta"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/retail":{"post":{"operationId":"pos.retail","summary":"Retail-only sale","tags":["POS & Retail"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"member_id":{"type":["string","null"]},"tender":{"type":"string","enum":["cash","card","wallet"]},"items":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":"string"},"quantity":{"type":"integer","minimum":1}},"required":["product_id","quantity"]},"minItems":1}},"required":["club_id","items"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sale_id":{"type":"string"},"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"gross_minor":{"type":"integer"},"currency":{"type":"string"}},"required":["sale_id","net_minor","vat_minor","gross_minor","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/quote":{"post":{"operationId":"pos.quote","summary":"Price a POS cart without committing \u2014 the order summary the cashier sees (RFP 06 / 03-17).\nCatalogue prices are VAT-inclusive, so the net/VAT/gross split mirrors the real receipt\n({@see Vat::fromGross})","tags":["POS & Retail"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"membership":{"type":"object","properties":{"plan_id":{"type":"string"},"member_id":{"type":["string","null"]}}},"retail":{"type":"object","properties":{"items":{"type":["array","null"],"items":{"type":"object","properties":{"product_id":{"type":"string"},"quantity":{"type":"integer","minimum":1}},"required":["product_id","quantity"]}}}}},"required":["club_id","retail"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"lines":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"membership"},"label":{"type":"string"},"label_ar":{"type":"string"},"qty":{"type":"integer","const":1},"unit_minor":{"type":"integer"},"line_minor":{"type":"integer"}},"required":["type","label","label_ar","qty","unit_minor","line_minor"]},{"type":"object","properties":{"type":{"type":"string","const":"fee"},"label":{"type":"string","const":"Joining fee"},"label_ar":{"type":"string","const":"\u0631\u0633\u0648\u0645 \u0627\u0644\u0627\u0646\u0636\u0645\u0627\u0645"},"qty":{"type":"integer","const":1},"unit_minor":{"type":"integer"},"line_minor":{"type":"integer"}},"required":["type","label","label_ar","qty","unit_minor","line_minor"]},{"type":"object","properties":{"type":{"type":"string","const":"retail"},"label":{"type":"string"},"label_ar":{"type":"string"},"qty":{"type":"integer"},"unit_minor":{"type":"integer"},"line_minor":{"type":"string"}},"required":["type","label","label_ar","qty","unit_minor","line_minor"]}]}},"subtotal_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"vat_rate":{"type":"integer"},"total_minor":{"type":"integer"},"currency":{"type":"string"}},"required":["lines","subtotal_minor","vat_minor","vat_rate","total_minor","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/sale":{"post":{"operationId":"pos.sale","summary":"Mixed cart: an optional membership and/or retail items in one POS transaction (RFP 06), settled\nby a single tender (cash / card / wallet). A wallet tender debits the member's store credit for\nthe gross total atomically \u2014 an insufficient balance rolls the whole sale back (422)","tags":["POS & Retail"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"member_id":{"type":["string","null"]},"sold_by_employee_id":{"type":["string","null"],"description":"Credit the sale to a rep (RFP #1) \u2014 null \u2192 the acting cashier's own employee. `assist`\nshares the membership commission per the split policy."},"assist_employee_id":{"type":["string","null"]},"coupon_code":{"type":["string","null"],"description":"Optional coupon on the membership line \u2014 validated + redeemed server-side.","maxLength":60},"tender":{"type":["string","null"],"enum":["cash","card","wallet"]},"payment_method":{"type":["string","null"],"description":"The specific payment method chosen (a catalogue key like nearpay/mada/cheque, or \"split\"),\nrecorded on the membership payment so every transaction knows its tender.","maxLength":40},"membership":{"type":"object","properties":{"member_id":{"type":"string"},"plan_id":{"type":"string"}}},"retail":{"type":"object","properties":{"member_id":{"type":["string","null"]},"items":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":"string"},"quantity":{"type":"integer","minimum":1}},"required":["product_id","quantity"]},"minItems":1}}},"payments":{"type":["array","null"],"description":"Split tender (RFP 03): multiple methods in one sale; the amounts must sum to the total.","items":{"type":"object","properties":{"method":{"type":"string","enum":["cash","card","wallet"]},"amount_minor":{"type":"integer","minimum":0}}},"minItems":1},"addons":{"type":["array","null"],"description":"Add-ons (PT packages, locker, \u2026) sold in the same cart \u2014 granted + VAT-recorded.","items":{"type":"object","properties":{"add_on_id":{"type":"string"},"quantity":{"type":"integer","minimum":1},"discount_minor":{"type":"integer","minimum":0}}}}},"required":["club_id","retail"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"membership":{"type":"object","properties":{"contract_id":{"type":"string"},"amount_minor":{"type":"string"},"discount_minor":{"type":"integer"}},"required":["contract_id","amount_minor","discount_minor"]},"retail":{"type":"object","properties":{"sale_id":{"type":"string"},"gross_minor":{"type":"integer"}},"required":["sale_id","gross_minor"]},"addons":{"type":"array","items":{"type":"object","properties":{"sale_id":{"type":"string"},"add_on_id":{"type":"string"},"total_minor":{"type":"integer"}},"required":["sale_id","add_on_id","total_minor"]}},"total_minor":{"type":"string"},"payments":{"type":"array","items":{}},"wallet_balance_minor":{"type":"integer"},"tender":{"anyOf":[{"type":"string"},{"type":"string","enum":["cash"]}]}},"required":["membership","retail","addons","total_minor","payments","wallet_balance_minor","tender"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/payment-config":{"get":{"operationId":"posPaymentConfig.forCashier","summary":"GET /pos/payment-config \u2014 what the till + enrol desk need to render their tenders","tags":["POS & Retail"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/shifts/open":{"post":{"operationId":"posShift.open","summary":"POST /pos/shifts/open \u2014 open a cash shift for the acting cashier","tags":["POS & Retail"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"opening_float_minor":{"type":"integer","minimum":0},"tolerance_minor":{"type":"integer","minimum":0}},"required":["club_id","opening_float_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"opened_at":{"type":"string"},"opening_float_minor":{"type":"integer"},"tolerance_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"}},"required":["id","club_id","opened_at","opening_float_minor","tolerance_minor","currency","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/shifts/current":{"get":{"operationId":"posShift.current","summary":"GET /pos/shifts/current \u2014 the cashier's open shift (BLIND: running cash total is hidden)","tags":["POS & Retail"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"shift":{"type":["object","null"],"properties":{"id":{"type":"string"},"club_id":{"type":"string"},"opened_at":{"type":"string"},"opening_float_minor":{"type":"integer"},"tolerance_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"}},"required":["id","club_id","opened_at","opening_float_minor","tolerance_minor","currency","status"]}},"required":["shift"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/shifts/{shift}/close":{"post":{"operationId":"posShift.close","summary":"POST /pos/shifts/{shift}/close \u2014 blind close; reveals the variance","tags":["POS & Retail"],"parameters":[{"name":"shift","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"counted_cash_minor":{"type":"integer","minimum":0}},"required":["counted_cash_minor"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"opened_at":{"type":"string"},"opening_float_minor":{"type":"integer"},"tolerance_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"cash_sales_minor":{"type":"integer"},"counted_cash_minor":{"type":["integer","null"]},"expected_cash_minor":{"type":["integer","null"]},"variance_minor":{"type":["integer","null"]},"variance_flagged":{"type":"boolean"},"closed_at":{"type":"string"},"reviewed_by":{"type":["integer","null"]},"reviewed_at":{"type":"string"},"manager_note":{"type":["string","null"]}},"required":["id","club_id","opened_at","opening_float_minor","tolerance_minor","currency","status","cash_sales_minor","counted_cash_minor","expected_cash_minor","variance_minor","variance_flagged","closed_at","reviewed_by","reviewed_at","manager_note"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/shifts":{"get":{"operationId":"posShift.index","summary":"GET /pos/shifts \u2014 manager board of shifts (optionally ?status=flagged)","tags":["POS & Retail"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"shifts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"opened_at":{"type":"string"},"opening_float_minor":{"type":"integer"},"tolerance_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"cash_sales_minor":{"type":"integer"},"counted_cash_minor":{"type":["integer","null"]},"expected_cash_minor":{"type":["integer","null"]},"variance_minor":{"type":["integer","null"]},"variance_flagged":{"type":"boolean"},"closed_at":{"type":"string"},"reviewed_by":{"type":["integer","null"]},"reviewed_at":{"type":"string"},"manager_note":{"type":["string","null"]}},"required":["id","club_id","opened_at","opening_float_minor","tolerance_minor","currency","status","cash_sales_minor","counted_cash_minor","expected_cash_minor","variance_minor","variance_flagged","closed_at","reviewed_by","reviewed_at","manager_note"]}}},"required":["shifts"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/shifts/{shift}/review":{"post":{"operationId":"posShift.review","summary":"POST /pos/shifts/{shift}/review \u2014 a manager acknowledges/notes a flagged shift","tags":["POS & Retail"],"parameters":[{"name":"shift","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":["string","null"],"maxLength":500}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"opened_at":{"type":"string"},"opening_float_minor":{"type":"integer"},"tolerance_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"cash_sales_minor":{"type":"integer"},"counted_cash_minor":{"type":["integer","null"]},"expected_cash_minor":{"type":["integer","null"]},"variance_minor":{"type":["integer","null"]},"variance_flagged":{"type":"boolean"},"closed_at":{"type":"string"},"reviewed_by":{"type":["integer","null"]},"reviewed_at":{"type":"string"},"manager_note":{"type":["string","null"]}},"required":["id","club_id","opened_at","opening_float_minor","tolerance_minor","currency","status","cash_sales_minor","counted_cash_minor","expected_cash_minor","variance_minor","variance_flagged","closed_at","reviewed_by","reviewed_at","manager_note"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/pos/addon-sale":{"post":{"operationId":"addOnSale.sell","summary":"POST /pos/addon-sale (permission:pos.sell). Sell `quantity` (default 1) of an\nadd-on to a member at a club. The add-on must be sellable today (#7) and apply to\nthe club (#8), and a PT add-on requires the member's active membership (#13, via\n{@see AddOnService::canGrant}). Price is VAT-inclusive: `total_minor` already\ncarries `vat_minor`. Returns the sale id, totals, and the produced grant ids","tags":["POS & Retail"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"add_on_id":{"type":"string"},"club_id":{"type":"string"},"quantity":{"type":"integer","minimum":1},"discount_minor":{"type":"integer","minimum":0},"employee_id":{"type":["string","null"]},"payment_method":{"type":["string","null"],"maxLength":40}},"required":["member_id","add_on_id","club_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sale_id":{"type":"string"},"total_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"grant_ids":{"type":"array","items":{"type":"string"}}},"required":["sale_id","total_minor","vat_minor","grant_ids"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/lockers":{"get":{"operationId":"locker.index","summary":"List the brand's lockers (?club_id, ?status filters), with the current assignee when assigned","tags":["POS & Retail"],"parameters":[{"name":"club_id","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/LockerStatus"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"lockers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"code":{"type":"string"},"size":{"type":"string"},"status":{"type":"string"},"monthly_fee_minor":{"type":"integer"},"current_assignment":{"type":["object","null"],"properties":{"id":{"type":"string"},"member_id":{"type":"string"},"assigned_at":{"type":"string"}},"required":["id","member_id","assigned_at"]}},"required":["id","club_id","code","size","status","monthly_fee_minor","current_assignment"]}}},"required":["lockers"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"locker.store","summary":"Create a locker for a club (config.manage)","tags":["POS & Retail"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"code":{"type":"string","maxLength":50},"size":{"type":"string","maxLength":50},"status":{"$ref":"#/components/schemas/LockerStatus"},"monthly_fee_minor":{"type":"integer","minimum":0}},"required":["club_id","code","size"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"code":{"type":"string"},"size":{"type":"string"},"status":{"type":"string"},"monthly_fee_minor":{"type":"integer"},"current_assignment":{"type":["object","null"],"properties":{"id":{"type":"string"},"member_id":{"type":"string"},"assigned_at":{"type":"string"}},"required":["id","member_id","assigned_at"]}},"required":["id","club_id","code","size","status","monthly_fee_minor","current_assignment"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/lockers/{locker}":{"patch":{"operationId":"locker.update","summary":"Update a locker's status or size (config.manage)","tags":["POS & Retail"],"parameters":[{"name":"locker","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/LockerStatus"},"size":{"type":"string","maxLength":50},"monthly_fee_minor":{"type":"integer","minimum":0}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"code":{"type":"string"},"size":{"type":"string"},"status":{"type":"string"},"monthly_fee_minor":{"type":"integer"},"current_assignment":{"type":["object","null"],"properties":{"id":{"type":"string"},"member_id":{"type":"string"},"assigned_at":{"type":"string"}},"required":["id","member_id","assigned_at"]}},"required":["id","club_id","code","size","status","monthly_fee_minor","current_assignment"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/lockers/{locker}/assign":{"post":{"operationId":"locker.assign","summary":"Assign a locker to a member (members.manage). 422 if the locker is not available.\nThe member must have a presence in this brand (a Contract or ProspectProfile),\nresolved within the brand scope. Sets status=assigned and opens an assignment","tags":["POS & Retail"],"parameters":[{"name":"locker","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"}},"required":["member_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"code":{"type":"string"},"size":{"type":"string"},"status":{"type":"string"},"monthly_fee_minor":{"type":"integer"},"current_assignment":{"type":["object","null"],"properties":{"id":{"type":"string"},"member_id":{"type":"string"},"assigned_at":{"type":"string"}},"required":["id","member_id","assigned_at"]}},"required":["id","club_id","code","size","status","monthly_fee_minor","current_assignment"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/lockers/{locker}/release":{"post":{"operationId":"locker.release","summary":"Release a locker (members.manage): close the open assignment, status=available","tags":["POS & Retail"],"parameters":[{"name":"locker","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"code":{"type":"string"},"size":{"type":"string"},"status":{"type":"string"},"monthly_fee_minor":{"type":"integer"},"current_assignment":{"type":["object","null"],"properties":{"id":{"type":"string"},"member_id":{"type":"string"},"assigned_at":{"type":"string"}},"required":["id","member_id","assigned_at"]}},"required":["id","club_id","code","size","status","monthly_fee_minor","current_assignment"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/products":{"get":{"operationId":"product.index","summary":"List ALL products (active + inactive) with total stock across clubs","tags":["POS & Retail"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sku":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"category":{"type":["string","null"]},"description":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"stock":{"type":"integer"},"stock_by_club":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["id","sku","name","name_ar","image_url","category","description","price_minor","currency","is_active","stock","stock_by_club"]}}},"required":["products"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"product.store","summary":"Create a product (brand_id auto-filled). Rejects a duplicate SKU within the brand","tags":["POS & Retail"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sku":{"type":"string","maxLength":64},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"],"format":"uri","maxLength":2048},"category":{"type":["string","null"],"maxLength":64},"description":{"type":["string","null"],"maxLength":2000},"price_minor":{"type":"integer","minimum":0},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"is_active":{"type":"boolean"}},"required":["sku","name","price_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"sku":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"category":{"type":["string","null"]},"description":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"stock":{"type":"integer"},"stock_by_club":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["id","sku","name","name_ar","image_url","category","description","price_minor","currency","is_active","stock","stock_by_club"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/products/{product}":{"get":{"operationId":"product.show","summary":"A single product with its per-club stock breakdown (for the edit screen)","tags":["POS & Retail"],"parameters":[{"name":"product","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"sku":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"category":{"type":["string","null"]},"description":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"stock":{"type":"integer"},"stock_by_club":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["id","sku","name","name_ar","image_url","category","description","price_minor","currency","is_active","stock","stock_by_club"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"product.update","summary":"Update a product's mutable fields. Resolved within the brand scope","tags":["POS & Retail"],"parameters":[{"name":"product","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"],"format":"uri","maxLength":2048},"category":{"type":["string","null"],"maxLength":64},"description":{"type":["string","null"],"maxLength":2000},"price_minor":{"type":"integer","minimum":0},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"sku":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"category":{"type":["string","null"]},"description":{"type":["string","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"stock":{"type":"integer"},"stock_by_club":{"type":"object","additionalProperties":{"type":"integer"}}},"required":["id","sku","name","name_ar","image_url","category","description","price_minor","currency","is_active","stock","stock_by_club"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/products/{product}/stock":{"post":{"operationId":"product.stock","summary":"Adjust a product's stock at a club and ledger the movement (RFP 07). Pass `received` to ADD that many\n(a receive), or `quantity` to SET the level (records the delta as an adjustment)","tags":["POS & Retail"],"parameters":[{"name":"product","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"quantity":{"type":["integer","null"],"minimum":0},"received":{"type":["integer","null"],"description":"SET the level to this"},"reason":{"type":["string","null"],"description":"ADD this many (negative = correction)","maxLength":255}},"required":["club_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"product_id":{"type":"string"},"club_id":{"type":"string"},"quantity":{"type":"integer"}},"required":["product_id","club_id","quantity"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/products/{product}/movements":{"get":{"operationId":"product.movements","summary":"Stock movement history for a product (receives + sales + adjustments), newest first","tags":["POS & Retail"],"parameters":[{"name":"product","in":"path","required":true,"schema":{"type":"string"}},{"name":"club_id","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"movements":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"club_id":{"type":"string"},"type":{"type":"string"},"quantity_delta":{"type":"integer"},"balance_after":{"type":"integer"},"reference_id":{"type":["string","null"]},"reason":{"type":["string","null"]},"at":{"type":"string"}},"required":["id","club_id","type","quantity_delta","balance_after","reference_id","reason","at"]}}},"required":["movements"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/payments/webhook/{gateway}":{"post":{"operationId":"payment.webhook_0","description":"Returns 200 {received:true} unconditionally so the gateway stops retrying once we\nhave it; settlement itself is idempotent.","summary":"Inbound gateway webhook \u2014 PUBLIC (no auth, no tenant middleware: gateways send no\nX-Brand header). We resolve the brand from the matched Payment, parse + verify the\nevent, then ALWAYS retrieve() the charge as the source of truth (defends against\nspoofed/forged callbacks and missed/late retries \u2014 RFP #7 ghost transactions)","tags":["Payments & Finance"],"parameters":[{"name":"gateway","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reference":{"type":"string","default":""}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"}},"required":["received"]}}}}}},"get":{"operationId":"payment.webhook_1","description":"Returns 200 {received:true} unconditionally so the gateway stops retrying once we\nhave it; settlement itself is idempotent.","summary":"Inbound gateway webhook \u2014 PUBLIC (no auth, no tenant middleware: gateways send no\nX-Brand header). We resolve the brand from the matched Payment, parse + verify the\nevent, then ALWAYS retrieve() the charge as the source of truth (defends against\nspoofed/forged callbacks and missed/late retries \u2014 RFP #7 ghost transactions)","tags":["Payments & Finance"],"parameters":[{"name":"gateway","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"}},"required":["received"]}}}}}}},"/checkout":{"post":{"operationId":"checkout.store","summary":"Sell a membership: draft \u2192 capture \u2192 settle, within the current brand","tags":["Payments & Finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"plan_id":{"type":"string"},"club_id":{"type":"string"},"channel":{"$ref":"#/components/schemas/SalesChannel"},"segment":{"$ref":"#/components/schemas/MemberSegment"}},"required":["member_id","plan_id","club_id","channel"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contract_id":{"type":"string"},"payment_id":{"type":"string"},"amount_minor":{"type":"string"},"currency":{"type":"string"}},"required":["contract_id","payment_id","amount_minor","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"202":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"This club is at capacity \u2014 the prospect was added to the waitlist."},"waitlisted":{"type":"boolean"},"entry_id":{"type":"string"},"position":{"type":"integer"}},"required":["message","waitlisted","entry_id","position"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/nearpay/config":{"get":{"operationId":"nearpay.config","summary":"GET /nearpay/config \u2014 what the Flutter app needs to init the NearPay SDK (no secrets)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"merchant_id":{"type":"string"},"environment":{"anyOf":[{"type":"string"},{"type":"string","enum":["sandbox"]}]},"keystore":{"type":["string","null"],"description":"The SoftPOS keystore file (base64) the device needs to initialise the NearPay SDK."}},"required":["enabled","merchant_id","environment","keystore"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/nearpay/transactions":{"get":{"operationId":"nearpay.index","summary":"GET /nearpay/transactions?status=&terminal_id=&reference= \u2014 list (the device polls pending intents)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"transactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"terminal_id":{"type":["string","null"]},"reference":{"type":["string","null"]},"nearpay_uuid":{"type":["string","null"]},"scheme":{"type":["string","null"]},"card_last4":{"type":["string","null"]},"payment_id":{"type":["string","null"]},"captured_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","status","type","amount_minor","currency","terminal_id","reference","nearpay_uuid","scheme","card_last4","payment_id","captured_at","created_at"]}}},"required":["transactions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"nearpay.store","summary":"POST /nearpay/transactions \u2014 the cashier opens a NearPay sale (a pending intent the device fulfils)","tags":["Payments & Finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer","minimum":1},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"reference":{"type":["string","null"],"maxLength":120},"terminal_id":{"type":["string","null"],"maxLength":120},"member_id":{"type":["string","null"]},"type":{"type":["string","null"],"enum":["purchase","refund"]}},"required":["amount_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"terminal_id":{"type":["string","null"]},"reference":{"type":["string","null"]},"nearpay_uuid":{"type":["string","null"]},"scheme":{"type":["string","null"]},"card_last4":{"type":["string","null"]},"payment_id":{"type":["string","null"]},"captured_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","status","type","amount_minor","currency","terminal_id","reference","nearpay_uuid","scheme","card_last4","payment_id","captured_at","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/nearpay/transactions/{nearpayTransaction}":{"get":{"operationId":"nearpay.show","summary":"GET /nearpay/transactions/{id} \u2014 the cashier polls for the device result","tags":["Payments & Finance"],"parameters":[{"name":"nearpayTransaction","in":"path","required":true,"description":"The nearpay transaction ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"terminal_id":{"type":["string","null"]},"reference":{"type":["string","null"]},"nearpay_uuid":{"type":["string","null"]},"scheme":{"type":["string","null"]},"card_last4":{"type":["string","null"]},"payment_id":{"type":["string","null"]},"captured_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","status","type","amount_minor","currency","terminal_id","reference","nearpay_uuid","scheme","card_last4","payment_id","captured_at","created_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/nearpay/transactions/{nearpayTransaction}/capture":{"post":{"operationId":"nearpay.capture","summary":"POST /nearpay/transactions/{id}/capture \u2014 the Flutter app reports the on-device tap result","tags":["Payments & Finance"],"parameters":[{"name":"nearpayTransaction","in":"path","required":true,"description":"The nearpay transaction ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"nearpay_uuid":{"type":["string","null"],"maxLength":191},"status":{"type":"string","enum":["captured","failed"]},"scheme":{"type":["string","null"],"maxLength":40},"card_last4":{"type":["string","null"],"minLength":4,"maxLength":4},"meta":{"type":["array","null"],"items":{"type":"string"}}},"required":["status"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"terminal_id":{"type":["string","null"]},"reference":{"type":["string","null"]},"nearpay_uuid":{"type":["string","null"]},"scheme":{"type":["string","null"]},"card_last4":{"type":["string","null"]},"payment_id":{"type":["string","null"]},"captured_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","status","type","amount_minor","currency","terminal_id","reference","nearpay_uuid","scheme","card_last4","payment_id","captured_at","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/nearpay/transactions/{nearpayTransaction}/refund":{"post":{"operationId":"nearpay.refund","summary":"POST /nearpay/transactions/{id}/refund \u2014 record a refund of a captured transaction (device-captured)","tags":["Payments & Finance"],"parameters":[{"name":"nearpayTransaction","in":"path","required":true,"description":"The nearpay transaction ID","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"nearpay_uuid":{"type":["string","null"],"maxLength":191},"meta":{"type":["array","null"],"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"terminal_id":{"type":["string","null"]},"reference":{"type":["string","null"]},"nearpay_uuid":{"type":["string","null"]},"scheme":{"type":["string","null"]},"card_last4":{"type":["string","null"]},"payment_id":{"type":["string","null"]},"captured_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","status","type","amount_minor","currency","terminal_id","reference","nearpay_uuid","scheme","card_last4","payment_id","captured_at","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/dunning":{"get":{"operationId":"dunning.index","summary":"Dunning state of every monthly-recurring contract (for the lifecycle board)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"contracts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member_id":{"type":"string"},"member_name":{"type":"string"},"plan_name":{"type":["string","null"]},"state":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"expiry_date":{"type":"string"},"renewal_failed_at":{"type":"string"},"grace_until":{"type":"string"},"in_grace":{"type":"boolean"},"past_grace":{"type":"boolean"}},"required":["id","member_id","member_name","plan_name","state","amount_minor","currency","expiry_date","renewal_failed_at","grace_until","in_grace","past_grace"]}}},"required":["contracts"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/dunning/events":{"get":{"operationId":"dunning.events","summary":"Recent lifecycle webhook events (the outbox) \u2014 shows \"events emitted on every transition\"","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event_name":{"type":"string"},"payload":{"type":"array","items":{}},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","event_name","payload","status","created_at"]}}},"required":["events"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/dunning/run-renewals":{"post":{"operationId":"dunning.runRenewals","summary":"The daily renewal sweep: charge every due monthly contract (primary \u2192 backup)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"processed":{"type":"integer","minimum":0},"renewed":{"type":"integer"},"grace":{"type":"integer"}},"required":["processed","renewed","grace"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/dunning/run-grace-expiry":{"post":{"operationId":"dunning.runGraceExpiry","summary":"The daily grace sweep: revoke + auto-cancel every contract whose grace window has lapsed (day-4)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"processed":{"type":"integer","minimum":0}},"required":["processed"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/dunning/reset":{"post":{"operationId":"dunning.reset","summary":"Re-park all dunning demo accounts to their starting states, so the scenario can be repeated","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"reset":{"type":"boolean"},"accounts":{"type":"integer"}},"required":["reset","accounts"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/dunning/contracts/{contract}/simulate-failure":{"post":{"operationId":"dunning.simulateFailure","summary":"Demo: force a contract's renewal to fail \u2192 it enters the 3-day grace (backup auto-attempt shown)","tags":["Payments & Finance"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"grace"},"grace_until":{"type":"string"}},"required":["status","grace_until"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"Only an active, non-grace contract can fail a renewal."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/dunning/contracts/{contract}/reactivate":{"post":{"operationId":"dunning.reactivate","summary":"Reactivate a member's membership. WITHIN grace (still active) \u2192 restore the same contract, no\njoining fee. AFTER grace (cancelled) \u2192 a fresh sale: new contract with the joining fee re-charged","tags":["Payments & Finance"],"parameters":[{"name":"contract","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"mode":{"type":"string","const":"new_contract"},"contract_id":{"type":"string"},"amount_minor":{"type":"string"},"joining_fee_minor":{"type":"integer"}},"required":["mode","contract_id","amount_minor","joining_fee_minor"]},{"type":"object","properties":{"mode":{"type":"string","const":"restored"},"contract_id":{"type":"string"},"joining_fee_minor":{"type":"integer"}},"required":["mode","contract_id","joining_fee_minor"]}]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"This membership is not eligible for reactivation."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/refunds/eligible":{"get":{"operationId":"refund.eligible","summary":"Active contracts eligible for a cooling-off refund (first membership, within the window)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"eligible":{"type":"array","items":{"type":"object","properties":{"contract_id":{"type":"string"},"member_name":{"type":"string"},"plan_name":{"type":["string","null"]},"amount_minor":{"type":"integer"},"refund_minor":{"type":"integer"},"currency":{"type":"string"},"activated_at":{"type":"string"}},"required":["contract_id","member_name","plan_name","amount_minor","refund_minor","currency","activated_at"]}}},"required":["eligible"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/refunds":{"get":{"operationId":"refund.index","summary":"Cooling-off refund requests + their outcome (credit note + deferred reversal once approved)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"},"member_name":{"type":["string","null"]},"reason_code":{},"destination":{},"iban":{},"amount_minor":{},"currency":{},"original_invoice_uuid":{},"decided_at":{"type":"string"},"credit_note":{"type":["object","null"],"properties":{"id":{"type":"string"},"uuid":{"type":"string"},"gross_minor":{"type":"string"},"references_invoice_id":{"type":"string"}},"required":["id","uuid","gross_minor","references_invoice_id"]},"deferred_reversed":{"type":"boolean"}},"required":["id","status","member_name","reason_code","destination","iban","amount_minor","currency","original_invoice_uuid","decided_at","credit_note","deferred_reversed"]}}},"required":["requests"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/pos-payment-config":{"get":{"operationId":"posPaymentConfig.show","summary":"GET /manage/pos-payment-config \u2014 the gym's config + the full catalogue for the settings screen","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"config":{"anyOf":[{"type":"object","properties":{"enabled_tenders":{"type":"array","items":{}},"default_tender":{"type":"string"},"allow_split":{"type":"boolean"},"settings":{"type":["array","null"],"items":{}}},"required":["enabled_tenders","default_tender","allow_split","settings"]},{"type":"object","properties":{"enabled_tenders":{"type":"array","prefixItems":[{"type":"string","const":"cash"},{"type":"string","const":"nearpay"},{"type":"string","const":"wallet"}],"minItems":3,"maxItems":3,"additionalItems":false},"default_tender":{"type":"string","const":"cash"},"allow_split":{"type":"boolean"},"settings":{"type":"null"}},"required":["enabled_tenders","default_tender","allow_split","settings"]}]},"available_tenders":{"type":"array","prefixItems":[{"type":"string","const":"cash"},{"type":"string","const":"mada"},{"type":"string","const":"nearpay"},{"type":"string","const":"terminal"},{"type":"string","const":"bank_transfer"},{"type":"string","const":"cheque"},{"type":"string","const":"wallet"},{"type":"string","const":"link"},{"type":"string","const":"tabby"},{"type":"string","const":"tamara"}],"minItems":10,"maxItems":10,"additionalItems":false},"catalogue":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"online":{"type":"string"},"desk":{"type":"string"}},"required":["key","online","desk"]}}},"required":["config","available_tenders","catalogue"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"posPaymentConfig.update","summary":"PUT /manage/pos-payment-config \u2014 upsert the gym's tender config","tags":["Payments & Finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"default_tender":{"type":"string"},"allow_split":{"type":"boolean"},"settings":{"type":["array","null"],"items":{"type":"string"}},"enabled_tenders":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["default_tender","enabled_tenders"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"config":{"type":"object","properties":{"enabled_tenders":{"type":"array","items":{}},"default_tender":{"type":"string"},"allow_split":{"type":"boolean"},"settings":{"type":["array","null"],"items":{}}},"required":["enabled_tenders","default_tender","allow_split","settings"]}},"required":["config"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/invoices/{invoice}":{"get":{"operationId":"invoice.show","summary":"Fetch an invoice (brand-scoped: another brand's invoice is invisible)","tags":["Payments & Finance"],"parameters":[{"name":"invoice","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"uuid":{"type":"string"},"type":{"type":"string"},"currency":{"type":"string"},"issued_at":{"type":"string"},"seller_name":{"type":"string"},"seller_vat_number":{"type":"string"},"net_minor":{"type":"string"},"vat_minor":{"type":"string"},"gross_minor":{"type":"string"},"vat_rate":{"type":"string"},"clearance_status":{"type":"string"},"qr_payload":{"type":"string"},"lines":{"type":"string"}},"required":["id","uuid","type","currency","issued_at","seller_name","seller_vat_number","net_minor","vat_minor","gross_minor","vat_rate","clearance_status","qr_payload","lines"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/nearpay":{"get":{"operationId":"nearpayConfig.show","summary":"GET /manage/nearpay \u2014 the gym's NearPay config (the API key itself is never returned)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"is_enabled":{"type":"boolean"},"merchant_id":{"type":"string"},"environment":{"anyOf":[{"type":"string"},{"type":"string","enum":["sandbox"]}]},"base_url":{"type":"string"},"has_api_key":{"type":"boolean"},"has_keystore":{"type":"boolean"},"default_sandbox_url":{"type":"string","description":"Platform defaults the gym can fall back to (shown as placeholders in the form)."},"default_production_url":{"type":"string"}},"required":["is_enabled","merchant_id","environment","base_url","has_api_key","has_keystore","default_sandbox_url","default_production_url"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"nearpayConfig.update","summary":"PUT /manage/nearpay \u2014 enable/disable + set merchant id, API key, keystore file, environment","tags":["Payments & Finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"is_enabled":{"type":"boolean"},"merchant_id":{"type":["string","null"],"maxLength":120},"api_key":{"type":["string","null"],"maxLength":500},"keystore":{"type":["string","null"],"maxLength":2000000},"environment":{"type":"string","description":"the SoftPOS keystore file (base64), encrypted","enum":["sandbox","production"]},"base_url":{"type":["string","null"],"format":"uri","maxLength":255}},"required":["is_enabled","environment"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"is_enabled":{"type":"boolean"},"merchant_id":{"type":"string"},"environment":{"anyOf":[{"type":"string"},{"type":"string","enum":["sandbox"]}]},"base_url":{"type":"string"},"has_api_key":{"type":"boolean"},"has_keystore":{"type":"boolean"},"default_sandbox_url":{"type":"string","description":"Platform defaults the gym can fall back to (shown as placeholders in the form)."},"default_production_url":{"type":"string"}},"required":["is_enabled","merchant_id","environment","base_url","has_api_key","has_keystore","default_sandbox_url","default_production_url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/nearpay/test":{"post":{"operationId":"nearpayConfig.test","summary":"POST /manage/nearpay/test \u2014 verify the saved credentials actually reach NearPay","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"ok":{"type":"boolean"},"message":{"type":"string"}},"required":["ok","message"]},{"type":"object","properties":{"ok":{"type":"boolean"},"message":{"type":"string","const":"Connected to NearPay."}},"required":["ok","message"]},{"type":"object","properties":{"ok":{"type":"boolean"},"message":{"type":"string","const":"Set the merchant ID + API key first."}},"required":["ok","message"]}]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/nearpay/terminals":{"get":{"operationId":"nearpayConfig.terminals","summary":"GET /manage/nearpay/terminals \u2014 the registered terminals (live from NearPay)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"terminals":{"type":"array","items":{}}},"required":["terminals"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/nearpay/transactions":{"get":{"operationId":"nearpayConfig.transactions","summary":"GET /manage/nearpay/transactions \u2014 the local NearPay transaction mirror, newest first","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"transactions":{"type":"array","items":{"$ref":"#/components/schemas/NearpayTransaction"}}},"required":["transactions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/nearpay/reconcile":{"post":{"operationId":"nearpayConfig.reconcile","summary":"POST /manage/nearpay/reconcile \u2014 trigger a settlement reconciliation with NearPay","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"result":{"type":"array","items":{}}},"required":["result"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/payments":{"get":{"operationId":"payment.index","summary":"The staff transactions ledger: real gateway transactions that happened on the platform, classified\nby payment method and transaction type, searchable by client. Defaults to money that actually moved\n(settled + refunded, non-zero) \u2014 abandoned/failed checkout attempts are hidden unless asked for via\nthe status filter. Filters are validated inline so they surface as query params in the API schema","tags":["Payments & Finance"],"parameters":[{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":120}},{"name":"method","in":"query","description":"client search: name / email / phone / ref","schema":{"type":["string","null"],"maxLength":40}},{"name":"type","in":"query","description":"payment method (card / tabby / \u2026)","schema":{"type":["string","null"],"maxLength":40}},{"name":"status","in":"query","description":"transaction type (purpose)","schema":{"type":["string","null"],"enum":["settled","refunded","pending","failed","cancelled","all"]}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"],"minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"payment_id":{"type":"string"},"status":{"type":"string"},"gateway":{"type":["string","null"]},"method":{"type":["string","null"]},"type":{"type":["string","null"]},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"member_id":{"anyOf":[{"type":"string"},{"type":"null"}]},"member_name":{"type":["string","null"]},"customer_email":{"type":["string","null"]},"reference":{"type":["string","null"]},"created_at":{"type":"string"},"settled_at":{"type":"string"}},"required":["payment_id","status","gateway","method","type","amount_minor","currency","member_id","member_name","customer_email","reference","created_at","settled_at"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"last_page":{"type":["object","null"]}},"required":["page","per_page","total","last_page"]},"methods":{"type":"array","description":"Distinct methods + types actually present, to populate the filter dropdowns.","items":{}},"types":{"type":"array","items":{}}},"required":["data","meta","methods","types"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/payments/initiate":{"post":{"operationId":"payment.initiate","summary":"Start a hosted-payment-page charge. Creates a pending Payment (brand auto-scoped),\nasks the resolved gateway to charge, and returns the redirect for the client","tags":["Payments & Finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"amount_minor":{"type":"integer","minimum":1},"currency":{"type":["string","null"],"minLength":3,"maxLength":3},"contract_id":{"type":["string","null"]},"member_id":{"type":["string","null"]},"gateway":{"type":["string","null"]},"customer_name":{"type":["string","null"]},"customer_email":{"type":["string","null"],"format":"email"},"customer_phone":{"type":["string","null"]},"save_card":{"type":["boolean","null"]},"description":{"type":["string","null"]}},"required":["amount_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"gateway":{"type":["string","null"]},"reference":{"type":["string","null"]},"redirect_url":{"type":["string","null"]},"status":{"type":"string"}},"required":["payment_id","gateway","reference","redirect_url","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/payments/{payment}/status":{"get":{"operationId":"payment.status","tags":["Payments & Finance"],"parameters":[{"name":"payment","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"payment_id":{"type":"string"},"status":{"type":"string"},"gateway":{"type":"string"},"method":{"type":"string"},"amount_minor":{"type":"string"},"currency":{"type":"string"},"redirect_url":{"type":"string"}},"required":["payment_id","status","gateway","method","amount_minor","currency","redirect_url"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/payments/{payment}/refund":{"post":{"operationId":"payment.refund","summary":"Refund a settled charge. `destination=original` (default) reverses it at the gateway;\n`destination=wallet` credits the member's store-credit wallet WITHOUT any gateway call\n(RFP 03 #19 cooling-off / goodwill refund) and marks the Payment refunded","tags":["Payments & Finance"],"parameters":[{"name":"payment","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"destination":{"type":["string","null"],"enum":["original","wallet"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"payment_id":{"type":"string"},"status":{"type":"string"},"gateway":{"type":"string"},"method":{"type":"string"},"amount_minor":{"type":"string"},"currency":{"type":"string"},"destination":{"type":"string","const":"original"}},"required":["payment_id","status","gateway","method","amount_minor","currency","destination"]},{"type":"object","properties":{"payment_id":{"type":"string"},"status":{"type":"string"},"gateway":{"type":"string"},"method":{"type":"string"},"amount_minor":{"type":"string"},"currency":{"type":"string"},"destination":{"type":"string","const":"wallet"},"balance_minor":{"type":"string"}},"required":["payment_id","status","gateway","method","amount_minor","currency","destination","balance_minor"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/payment-gateways":{"get":{"operationId":"payment.gateways","summary":"Operator-console listing of the gateways available to the current brand (RFP #4-admin)","tags":["Payments & Finance"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}}},"required":["data"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"payment.upsertGateway","summary":"Create/update a per-brand gateway configuration. Secret material is never echoed back","tags":["Payments & Finance"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"gateway":{"type":"string"},"label":{"type":["string","null"]},"mode":{"type":"string","enum":["test","live"]},"is_active":{"type":["boolean","null"]},"is_default":{"type":["boolean","null"]},"public_key":{"type":["string","null"]},"secret_key":{"type":["string","null"]},"webhook_secret":{"type":["string","null"]}},"required":["gateway","mode"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"gateway":{"type":"string"},"label":{"type":["string","null"]},"kind":{"type":"string"},"mode":{"type":"string"},"is_active":{"type":"boolean"},"is_default":{"type":"boolean"},"public_key":{"type":["string","null"]}},"required":["id","gateway","label","kind","mode","is_active","is_default","public_key"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/payment-methods/{method}":{"patch":{"operationId":"paymentMethod.update","summary":"Promote a card to the member's primary or backup slot, then return the refreshed list","tags":["Payments & Finance"],"parameters":[{"name":"method","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["primary","backup"]}},"required":["role"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gateway":{"type":"string"},"scheme":{"type":["string","null"]},"last4":{"type":["string","null"]},"exp_month":{"type":["string","null"]},"exp_year":{"type":["string","null"]},"is_primary":{"type":"boolean"},"is_backup":{"type":"boolean"}},"required":["id","gateway","scheme","last4","exp_month","exp_year","is_primary","is_backup"]}}},"required":["data"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"paymentMethod.destroy","summary":"Remove a stored card. Guard (RFP #20/#23): a member on an Active recurring contract must\nkeep at least one card on file, so deleting their LAST active card is rejected (422)","tags":["Payments & Finance"],"parameters":[{"name":"method","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gateway":{"type":"string"},"scheme":{"type":["string","null"]},"last4":{"type":["string","null"]},"exp_month":{"type":["string","null"]},"exp_year":{"type":["string","null"]},"is_primary":{"type":"boolean"},"is_backup":{"type":"boolean"}},"required":["id","gateway","scheme","last4","exp_month","exp_year","is_primary","is_backup"]}}},"required":["data"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"A member on a recurring plan must keep at least one card on file."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/brands":{"get":{"operationId":"brandPlatform.index","summary":"All brands across the platform, each with usage counts + subscription summary","tags":["Platform (operator)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"brands":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"is_active":{"type":"boolean"},"club_count":{"type":"integer","minimum":0},"member_count":{"type":"integer"},"subscription":{"type":["object","null"],"properties":{"tier":{"type":"string"},"status":{"type":"string"},"renews_at":{"type":"string"},"monthly_price_minor":{"type":"integer"}},"required":["tier","status","renews_at","monthly_price_minor"]},"owner":{"type":["object","null"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"mobile":{"type":["string","null"]}},"required":["name","email","mobile"]}},"required":["id","code","name","is_active","club_count","member_count","subscription","owner"]}}},"required":["brands"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"brandPlatform.store","summary":"Provision a new gym: create the Brand (active) + a subscription on the chosen tier","tags":["Platform (operator)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":50},"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"tier":{"type":"string"}},"required":["code","name","tier"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"is_active":{"type":"boolean"},"club_count":{"type":"integer","minimum":0},"member_count":{"type":"integer"},"subscription":{"type":["object","null"],"properties":{"tier":{"type":"string"},"status":{"type":"string"},"renews_at":{"type":"string"},"monthly_price_minor":{"type":"integer"}},"required":["tier","status","renews_at","monthly_price_minor"]},"owner":{"type":["object","null"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"mobile":{"type":["string","null"]}},"required":["name","email","mobile"]}},"required":["id","code","name","is_active","club_count","member_count","subscription","owner"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/brands/{brand}":{"patch":{"operationId":"brandPlatform.update","summary":"Suspend/reactivate (is_active, flag-only) or rename a brand","tags":["Platform (operator)"],"parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"is_active":{"type":"boolean"},"name":{"type":"string","maxLength":120},"code":{"type":"string","description":"The code is the tenant key (X-Brand) \u2014 editable, but must stay unique.","maxLength":40}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"is_active":{"type":"boolean"},"club_count":{"type":"integer","minimum":0},"member_count":{"type":"integer"},"subscription":{"type":["object","null"],"properties":{"tier":{"type":"string"},"status":{"type":"string"},"renews_at":{"type":"string"},"monthly_price_minor":{"type":"integer"}},"required":["tier","status","renews_at","monthly_price_minor"]},"owner":{"type":["object","null"],"properties":{"name":{"type":"string"},"email":{"type":"string"},"mobile":{"type":["string","null"]}},"required":["name","email","mobile"]}},"required":["id","code","name","is_active","club_count","member_count","subscription","owner"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/brands/{brand}/branding":{"get":{"operationId":"branding.operatorShow","summary":"GET /platform/brands/{brand}/branding \u2014 operator view of a specific gym's branding","tags":["Platform (operator)"],"parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"primary_color":{"type":"string"},"logo":{"type":"string"},"portal_name":{"type":"string"},"portal_name_ar":{"type":"string"}},"required":["primary_color","logo","portal_name","portal_name_ar"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"branding.update","summary":"PUT /platform/brands/{brand}/branding \u2014 operator sets a gym's main colour / logo / portal name","tags":["Platform (operator)"],"parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"primary_color":{"type":"string"},"logo":{"type":"string"},"portal_name":{"type":"string"},"portal_name_ar":{"type":"string"}},"required":["primary_color","logo","portal_name","portal_name_ar"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"branding.destroy","summary":"DELETE /platform/brands/{brand}/branding \u2014 reset to the Memvera default","tags":["Platform (operator)"],"parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"primary_color":{"type":"string"},"logo":{"type":"string"},"portal_name":{"type":"string"},"portal_name_ar":{"type":"string"}},"required":["primary_color","logo","portal_name","portal_name_ar"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/subscriptions":{"get":{"operationId":"subscription.index","summary":"All subscriptions across the platform, each with a brand summary","tags":["Platform (operator)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"tier":{"type":"string"},"status":{"type":"string"},"started_at":{"type":"string"},"renews_at":{"type":"string"},"monthly_price_minor":{"type":"integer"},"max_clubs":{"type":["integer","null"]},"max_members":{"type":["integer","null"]},"module_overrides":{"type":"array","items":{}},"brand":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","code","name","is_active"]}},"required":["id","brand_id","tier","status","started_at","renews_at","monthly_price_minor","max_clubs","max_members","module_overrides","brand"]}}},"required":["subscriptions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"subscription.store","summary":"Create a subscription for a brand, or change the brand's existing one to a\nnew tier (re-snapshotting limits/price from that tier)","tags":["Platform (operator)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"brand_id":{"type":"string"},"tier":{"type":"string"}},"required":["brand_id","tier"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"tier":{"type":"string"},"status":{"type":"string"},"started_at":{"type":"string"},"renews_at":{"type":"string"},"monthly_price_minor":{"type":"integer"},"max_clubs":{"type":["integer","null"]},"max_members":{"type":["integer","null"]},"module_overrides":{"type":"array","items":{}},"brand":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","code","name","is_active"]}},"required":["id","brand_id","tier","status","started_at","renews_at","monthly_price_minor","max_clubs","max_members","module_overrides","brand"]}}}},"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"tier":{"type":"string"},"status":{"type":"string"},"started_at":{"type":"string"},"renews_at":{"type":"string"},"monthly_price_minor":{"type":"integer"},"max_clubs":{"type":["integer","null"]},"max_members":{"type":["integer","null"]},"module_overrides":{"type":"array","items":{}},"brand":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","code","name","is_active"]}},"required":["id","brand_id","tier","status","started_at","renews_at","monthly_price_minor","max_clubs","max_members","module_overrides","brand"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/subscriptions/{subscription}":{"patch":{"operationId":"subscription.update","summary":"Adjust a subscription. Changing `tier` (plan code) re-snapshots limits/price\nfrom the plan unless the request explicitly overrides them. `module_overrides`\nlets the operator grant/revoke individual modules for this brand on top of the plan","tags":["Platform (operator)"],"parameters":[{"name":"subscription","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tier":{"type":"string"},"status":{"$ref":"#/components/schemas/SubscriptionStatus"},"renews_at":{"type":["string","null"],"format":"date-time"},"monthly_price_minor":{"type":"integer","minimum":0},"max_clubs":{"type":["integer","null"],"minimum":0},"max_members":{"type":["integer","null"],"minimum":0},"module_overrides":{"type":["array","null"],"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"tier":{"type":"string"},"status":{"type":"string"},"started_at":{"type":"string"},"renews_at":{"type":"string"},"monthly_price_minor":{"type":"integer"},"max_clubs":{"type":["integer","null"]},"max_members":{"type":["integer","null"]},"module_overrides":{"type":"array","items":{}},"brand":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","code","name","is_active"]}},"required":["id","brand_id","tier","status","started_at","renews_at","monthly_price_minor","max_clubs","max_members","module_overrides","brand"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/plans":{"get":{"operationId":"subscriptionPlan.index","summary":"GET /platform/plans \u2014 every plan + the gateable module catalogue","tags":["Platform (operator)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"tagline":{"type":["string","null"]},"tagline_ar":{"type":["string","null"]},"monthly_price_minor":{"type":"integer"},"currency":{"type":"string"},"max_clubs":{"type":["integer","null"]},"max_members":{"type":["integer","null"]},"included_modules":{"type":"array","items":{}},"is_public":{"type":"boolean"},"is_active":{"type":"boolean"},"sort":{"type":"integer"},"brand_count":{"type":"integer"}},"required":["id","code","name","name_ar","tagline","tagline_ar","monthly_price_minor","currency","max_clubs","max_members","included_modules","is_public","is_active","sort","brand_count"]}},"modules":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"group":{"type":["string","null"]},"is_core":{"type":"boolean"}},"required":["key","name","name_ar","group","is_core"]}}},"required":["plans","modules"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"subscriptionPlan.store","summary":"POST /platform/plans \u2014 create a custom plan","tags":["Platform (operator)"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"tagline":{"type":["string","null"]},"tagline_ar":{"type":["string","null"]},"monthly_price_minor":{"type":"integer"},"currency":{"type":"string"},"max_clubs":{"type":["integer","null"]},"max_members":{"type":["integer","null"]},"included_modules":{"type":"array","items":{}},"is_public":{"type":"boolean"},"is_active":{"type":"boolean"},"sort":{"type":"integer"},"brand_count":{"type":"integer"}},"required":["id","code","name","name_ar","tagline","tagline_ar","monthly_price_minor","currency","max_clubs","max_members","included_modules","is_public","is_active","sort","brand_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/plans/{plan}":{"patch":{"operationId":"subscriptionPlan.update","summary":"PATCH /platform/plans/{plan} \u2014 edit price/limits/name/modules/visibility","tags":["Platform (operator)"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"tagline":{"type":["string","null"]},"tagline_ar":{"type":["string","null"]},"monthly_price_minor":{"type":"integer"},"currency":{"type":"string"},"max_clubs":{"type":["integer","null"]},"max_members":{"type":["integer","null"]},"included_modules":{"type":"array","items":{}},"is_public":{"type":"boolean"},"is_active":{"type":"boolean"},"sort":{"type":"integer"},"brand_count":{"type":"integer","minimum":0}},"required":["id","code","name","name_ar","tagline","tagline_ar","monthly_price_minor","currency","max_clubs","max_members","included_modules","is_public","is_active","sort","brand_count"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"subscriptionPlan.destroy","summary":"DELETE /platform/plans/{plan} \u2014 remove a plan. Blocked while any brand is on it\n(retire it via is_active=false instead, which keeps existing brands but hides it\nfrom new provisioning/signup)","tags":["Platform (operator)"],"parameters":[{"name":"plan","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string"}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/invoices":{"get":{"operationId":"platformInvoice.index","summary":"All invoices across the platform, newest first, optionally filtered","tags":["Platform (operator)"],"parameters":[{"name":"brand_id","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/TenantInvoiceStatus"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"invoices":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"tenant_subscription_id":{"type":["string","null"]},"number":{"type":"string"},"period_start":{"type":"string"},"period_end":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"issued_at":{"type":"string"},"due_at":{"type":"string"},"paid_at":{"type":"string"},"brand":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]}},"required":["id","brand_id","tenant_subscription_id","number","period_start","period_end","amount_minor","currency","status","issued_at","due_at","paid_at","brand"]}}},"required":["invoices"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"platformInvoice.store","summary":"Manually raise an invoice against a brand (status due)","tags":["Platform (operator)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"brand_id":{"type":"string"},"amount_minor":{"type":"integer","minimum":0},"period_start":{"type":["string","null"],"format":"date-time"},"period_end":{"type":["string","null"],"format":"date-time"},"due_at":{"type":["string","null"],"format":"date-time"}},"required":["brand_id","amount_minor"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"tenant_subscription_id":{"type":["string","null"]},"number":{"type":"string"},"period_start":{"type":"string"},"period_end":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"issued_at":{"type":"string"},"due_at":{"type":"string"},"paid_at":{"type":"string"},"brand":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]}},"required":["id","brand_id","tenant_subscription_id","number","period_start","period_end","amount_minor","currency","status","issued_at","due_at","paid_at","brand"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/invoices/run-monthly":{"post":{"operationId":"platformInvoice.runMonthly","summary":"Demo trigger for the monthly run \u2014 invokes the same generator the\n`memvera:tenant-invoices` command uses. Returns the count created","tags":["Platform (operator)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"}},"required":["created"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/invoices/{invoice}":{"patch":{"operationId":"platformInvoice.update","summary":"Transition an invoice's status \u2014 mark paid (stamps paid_at), overdue, or\nvoid. Moving away from paid clears paid_at","tags":["Platform (operator)"],"parameters":[{"name":"invoice","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/TenantInvoiceStatus"}},"required":["status"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"tenant_subscription_id":{"type":["string","null"]},"number":{"type":"string"},"period_start":{"type":"string"},"period_end":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"issued_at":{"type":"string"},"due_at":{"type":"string"},"paid_at":{"type":"string"},"brand":{"type":["object","null"],"properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"}},"required":["id","code","name"]}},"required":["id","brand_id","tenant_subscription_id","number","period_start","period_end","amount_minor","currency","status","issued_at","due_at","paid_at","brand"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/demo-requests":{"get":{"operationId":"demoRequest.index","summary":"GET /platform/demo-requests \u2014 inbound enquiries for the admin console (platform.manage)","tags":["Platform (operator)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"company":{"type":"string"},"email":{"type":"string"},"phone":{"type":["string","null"]},"city":{"type":["string","null"]},"branches":{"type":["string","null"]},"message":{"type":["string","null"]},"status":{"type":"string"},"notes":{"type":["string","null"]},"handled_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","name","company","email","phone","city","branches","message","status","notes","handled_at","created_at"]}}},"required":["requests"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/platform/demo-requests/{demoRequest}":{"patch":{"operationId":"demoRequest.update","summary":"PATCH /platform/demo-requests/{demoRequest} \u2014 update follow-up status / notes","tags":["Platform (operator)"],"parameters":[{"name":"demoRequest","in":"path","required":true,"description":"The demo request ID","schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"notes":{"type":["string","null"],"maxLength":5000}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"request":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"company":{"type":"string"},"email":{"type":"string"},"phone":{"type":["string","null"]},"city":{"type":["string","null"]},"branches":{"type":["string","null"]},"message":{"type":["string","null"]},"status":{"type":"string"},"notes":{"type":["string","null"]},"handled_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","name","company","email","phone","city","branches","message","status","notes","handled_at","created_at"]}},"required":["ok","request"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/public/app/onboarding/{slide}/image":{"get":{"operationId":"mobileApp.slideImage","summary":"GET /public/app/onboarding/{slide}/image \u2014 serves the slide image as bytes so the\napp fetches a URL, not a 100KB+ base64 blob in JSON. Images are stored as data URLs\n(staff upload) \u2014 decode here; an external https image is redirected. Public + resolved\nfrom the slide id (an <img> carries no auth/tenant header \u2014 same pattern as the\nworkout-media proxy), with long immutable cache","tags":["Public (no auth)"],"parameters":[{"name":"slide","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/public/brands/{brand}/logo":{"get":{"operationId":"publicCatalogue.brandLogo","summary":"Brand logo image bytes, resolved by brand code in the path \u2014 an <img>/app carries no tenant\nheader, so the URL is self-describing. Serves a stored base64 data-URI as real image bytes and\nredirects to an already-hosted http(s) logo. This is the target of /public/brand's logo_url","tags":["Public (no auth)"],"parameters":[{"name":"brand","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/public/brand":{"get":{"operationId":"publicCatalogue.brand","summary":"Brand identity + theme for the brand-aware portal (RFP 24 #4) \u2014 name, logo, accent colour","tags":["Public (no auth)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"logo_url":{"type":["string","null"]},"primary_color":{"type":"string"},"portal_name":{"type":"string"},"portal_name_ar":{"type":"string"},"default_locale":{"type":"string"}},"required":["code","name","name_ar","logo_url","primary_color","portal_name","portal_name_ar","default_locale"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/public/branding":{"get":{"operationId":"branding.show","summary":"GET /public/branding \u2014 the current brand's branding (resolved from X-Brand). No auth","tags":["Public (no auth)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"primary_color":{"type":"string"},"logo":{"type":"string"},"portal_name":{"type":"string"},"portal_name_ar":{"type":"string"}},"required":["primary_color","logo","portal_name","portal_name_ar"]}}}}}}},"/public/hero-slides":{"get":{"operationId":"heroSlide.publicIndex","summary":"GET /public/hero-slides \u2014 the brand's ACTIVE slides for the landing (public, by X-Brand)","tags":["Public (no auth)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"slides":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"media_url":{"type":"string"},"poster_url":{"type":["string","null"]},"headline":{"type":["string","null"]},"headline_ar":{"type":["string","null"]},"subcopy":{"type":["string","null"]},"subcopy_ar":{"type":["string","null"]},"cta_label":{"type":["string","null"]},"cta_label_ar":{"type":["string","null"]},"cta_href":{"type":["string","null"]},"sort":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","type","media_url","poster_url","headline","headline_ar","subcopy","subcopy_ar","cta_label","cta_label_ar","cta_href","sort","is_active"]}}},"required":["slides"]}}}}}}},"/public/app/onboarding":{"get":{"operationId":"mobileApp.publicOnboarding","summary":"GET /public/app/onboarding \u2014 active slides for the current brand (resolved from X-Brand)","tags":["Public (no auth)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"slides":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"body":{"type":["string","null"]},"body_ar":{"type":["string","null"]},"image":{"type":["string","null"]},"image_url":{"type":["string","null"]},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","title","title_ar","body","body_ar","image","image_url","sort_order","is_active"]}}},"required":["slides"]}}}}}}},"/public/app/version":{"get":{"operationId":"mobileApp.publicVersion","summary":"GET /public/app/version?platform=ios&version=1.2.3 \u2014 the app's launch update check.\nReturns the decision for the supplied current version: none | soft | force","tags":["Public (no auth)"],"parameters":[{"name":"platform","in":"query","required":true,"schema":{"type":"string","enum":["ios","android"]}},{"name":"version","in":"query","schema":{"type":["string","null"],"maxLength":20}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"update":{"type":"string","const":"soft"},"latest_version":{"type":["string","null"],"description":"none | soft | force"},"min_version":{"type":["string","null"]},"store_url":{"type":["string","null"]},"message":{"type":["string","null"]},"message_ar":{"type":["string","null"]}},"required":["update","latest_version","min_version","store_url","message","message_ar"]},{"type":"object","properties":{"update":{"type":"string","const":"none"}},"required":["update"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/payment-methods":{"get":{"operationId":"paymentMethods.available","summary":"GET /public/payment-methods \u2014 the gym's available online methods + the active (default) gateway","tags":["Public (no auth)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"default_gateway":{"type":["string","null"]},"methods":{"type":"array","items":{"type":"object","properties":{"gateway":{"type":"string"},"label":{"type":"string"},"methods":{"type":"string"},"is_default":{"type":"boolean"},"is_bnpl":{"type":"boolean"},"min_amount_minor":{"type":["integer","null"],"description":"BNPL eligibility window (RFP 03 #8) \u2014 the app hides the option for an out-of-range order.","const":10000},"max_amount_minor":{"type":["integer","null"],"description":"SAR 100","const":500000}},"required":["gateway","label","methods","is_default","is_bnpl","min_amount_minor","max_amount_minor"]}},"currency":{"type":"string","const":"SAR"}},"required":["default_gateway","methods","currency"]}}}}}}},"/public/branches":{"get":{"operationId":"publicCatalogue.branches","summary":"Active branches (clubs) the prospect can pick as their home club, with map coordinates.\nPass ?gender=male|female to get only clubs that admit that gender \u2014 clients that know\nthe buyer should use it so segregated clubs never appear as selectable","tags":["Public (no auth)"],"parameters":[{"name":"gender","in":"query","schema":{"type":["string","null"],"enum":["male","female"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"branches":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"city":{"type":["string","null"]},"address":{"type":["string","null"]},"gender_designation":{"type":"string"},"latitude":{"type":["number","null"]},"longitude":{"type":["number","null"]},"zones_count":{"type":"integer"}},"required":["id","code","name","name_ar","city","address","gender_designation","latitude","longitude","zones_count"]}}},"required":["branches"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/plans":{"get":{"operationId":"publicCatalogue.plans","summary":"Web-visible, sellable membership plans with an indicative \"from\" price at the Web channel.\nPass ?gender=male|female to get only the plans that gender may buy \u2014 gender-neutral plans\n(no restriction) plus the ones restricted to that gender. Mirrors the branches lookup so the\napp can show a member only the memberships they can actually check out with","tags":["Public (no auth)"],"parameters":[{"name":"gender","in":"query","schema":{"type":["string","null"],"enum":["male","female"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"plans":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"duration_months":{"type":["integer","null"]},"duration_days":{"type":["integer","null"]},"billing_mode":{"type":"string"},"gender":{"type":"string"},"amenities":{"type":"array","items":{}},"price_minor":{"type":"null"},"currency":{"type":"string","const":"SAR"},"age_min":{"type":["integer","null"]},"age_max":{"type":["integer","null"]}},"required":["id","code","name","name_ar","image_url","duration_months","duration_days","billing_mode","gender","amenities","price_minor","currency","age_min","age_max"]}}},"required":["plans"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/addons":{"get":{"operationId":"publicCatalogue.addons","summary":"Active optional add-ons a prospect can bundle into the join (PT packs, classes, locker\u2026).\nPass ?kind=pt to list only PT packages, and ?gender=male|female to drop add-ons restricted\nto the other gender (a gender-less add-on is available to everyone). Lets the app render a\ngender-appropriate \"Personal Training\" lookup with a single call","tags":["Public (no auth)"],"parameters":[{"name":"gender","in":"query","schema":{"type":["string","null"],"enum":["male","female"]}},{"name":"kind","in":"query","schema":{"$ref":"#/components/schemas/AddOnKind"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"addons":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"kind":{"type":"string"},"gender":{"type":"string"},"credits":{"type":["integer","null"]},"price_minor":{"type":"integer"},"currency":{"type":"string"}},"required":["id","code","name","name_ar","image_url","kind","gender","credits","price_minor","currency"]}}},"required":["addons"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/legal/{type}":{"get":{"operationId":"legal.show","summary":"Public (member portal + mobile app): a single document for the current brand","tags":["Public (no auth)"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":"string"},"body":{"type":"string"},"body_ar":{"type":"string"},"updated_at":{"type":"string"}},"required":["type","title","title_ar","body","body_ar","updated_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/public/trainers":{"get":{"operationId":"publicCatalogue.trainers","summary":"GET /public/trainers \u2014 the brand's coaches for the public storefront \"Coaches\" page","tags":["Public (no auth)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"trainers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"title":{"type":["string","null"]},"photo_url":{"type":["string","null"]},"bio":{"type":["string","null"]},"bio_ar":{"type":["string","null"]},"certifications":{"type":["string","null"]},"certifications_ar":{"type":["string","null"]},"club":{"type":"string"}},"required":["id","name","name_ar","title","photo_url","bio","bio_ar","certifications","certifications_ar","club"]}}},"required":["trainers"]}}}}}}},"/public/class-types":{"get":{"operationId":"publicCatalogue.classTypes","summary":"GET /public/class-types \u2014 the brand's active GROUP class types for the storefront \"Classes\"\nsection (excludes 1-on-1 personal training, which isn't a storefront class)","tags":["Public (no auth)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"class_types":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"category":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]}},"required":["id","name","name_ar","category","category_ar","image_url"]}}},"required":["class_types"]}}}}}}},"/public/join/quote":{"post":{"operationId":"publicJoin.quote","summary":"Live order quote (RFP 24 #8/#12) \u2014 the checkout \"fee breakdown\" step. Returns the plan +\nadd-on lines, an optional validated promo discount, and the net / VAT / gross split (prices are\nVAT-inclusive, so VAT is backed out of the gross). Promo validation is server-side (never a\nclient mirror): an invalid code returns a clear reason without breaking the quote","tags":["Public (no auth)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan_id":{"type":"string"},"club_id":{"type":["string","null"],"description":"Optional: with a club the quote uses that branch's location-scoped price; without one it\nfalls back to the brand/default \"from\" price (same as /public/plans) so the app can preview\na price before the buyer has picked a branch."},"coupon_code":{"type":["string","null"],"maxLength":60},"gender":{"type":["string","null"],"description":"Optional but recommended: lets the quote fail fast on gender segregation\ninstead of the buyer discovering it at store() after reviewing a price.","enum":["male","female"]},"add_on_ids":{"type":["array","null"],"items":{"type":"string"}}},"required":["plan_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"currency":{"type":"string"},"lines":{"type":"array","prefixItems":[{"type":"object","properties":{"type":{"type":"string","const":"addon"},"id":{"type":"string"},"label":{"type":"string"},"gross_minor":{"type":"integer"}},"required":["type","id","label","gross_minor"]}],"minItems":1,"maxItems":1,"additionalItems":false},"subtotal_minor":{"type":"integer"},"discount":{"type":"object","properties":{"code":{"type":"string"},"amount_minor":{"type":"integer"}},"required":["code","amount_minor"]},"coupon_error":{"type":"string"},"joining_fee_minor":{"type":"integer"},"total_minor":{"type":"string"},"vat":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"gross_minor":{"type":"integer"},"rate_percent":{"type":"integer"}},"required":["net_minor","vat_minor","gross_minor","rate_percent"]}},"required":["currency","lines","subtotal_minor","discount","coupon_error","joining_fee_minor","total_minor","vat"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/public/join/identify":{"post":{"operationId":"publicJoin.identify","summary":"Returning-customer check (RFP 24 #21) \u2014 given any identifier the prospect typed, says whether a\nmember already exists in this brand, so the funnel can offer \"sign in\" instead of re-registering.\nBoolean-only by design (no PII leaked to an unauthenticated caller)","tags":["Public (no auth)"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"national_id":{"type":["string","null"],"maxLength":20},"iqama_number":{"type":["string","null"],"maxLength":20},"mobile":{"type":["string","null"],"maxLength":20},"email":{"type":["string","null"],"format":"email","maxLength":160}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"exists":{"type":"string"}},"required":["exists"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/join":{"post":{"operationId":"publicJoin.store","tags":["Public (no auth)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","maxLength":120},"last_name":{"type":["string","null"],"maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"gender":{"$ref":"#/components/schemas/Gender"},"national_id":{"type":["string","null"]},"iqama_number":{"type":["string","null"],"maxLength":20},"mobile":{"type":["string","null"],"maxLength":20},"email":{"type":["string","null"],"format":"email","maxLength":160},"nationality":{"type":["string","null"],"maxLength":60},"dob":{"type":["string","null"],"format":"date-time"},"plan_id":{"type":"string"},"club_id":{"type":"string"},"coupon_code":{"type":["string","null"],"maxLength":60},"pdpl_consent":{"type":["boolean","null"]},"password":{"type":["string","null"],"description":"Optional self-set login password (RFP 24 #21) \u2014 used only when the member has none yet.","minLength":8,"maxLength":72},"gateway":{"type":["string","null"],"description":"Chosen gateway (card processor or BNPL) the brand has enabled; null = the default card processor."},"ref":{"type":["string","null"],"description":"Referral code from a member-get-member invite link (RFP 19) \u2014 attributed on signup.","maxLength":32},"add_on_ids":{"type":["array","null"],"items":{"type":"string"}},"utm":{"type":["array","null"],"items":{"type":["string","null"],"maxLength":200}}},"required":["first_name","gender","plan_id","club_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"is_existing":{"type":"boolean"},"contract_id":{"type":"string"},"payment_id":{"type":"string"},"amount_minor":{"type":["object","null"]},"discount_minor":{"type":"integer"},"currency":{"type":"string"},"redirect_url":{"type":"string"}},"required":["member_id","is_existing","contract_id","payment_id","amount_minor","discount_minor","currency","redirect_url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/public/register":{"post":{"operationId":"publicRegister.register","tags":["Public (no auth)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","maxLength":120},"last_name":{"type":"string","maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"gender":{"$ref":"#/components/schemas/Gender"},"club_id":{"type":"string"},"national_id":{"type":["string","null"]},"iqama_number":{"type":["string","null"],"maxLength":20},"mobile":{"type":"string","maxLength":30},"email":{"type":["string","null"],"format":"email","maxLength":190},"nationality":{"type":["string","null"],"maxLength":60},"dob":{"type":["string","null"],"format":"date-time"},"password":{"type":"string","minLength":8,"maxLength":72},"pdpl_consent":{"type":"string"}},"required":["first_name","last_name","gender","club_id","mobile","password","pdpl_consent"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"member":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"first_name_ar":{"type":["string","null"]},"last_name_ar":{"type":["string","null"]},"email":{"type":["string","null"]},"mobile":{"type":["string","null"]},"language_pref":{"type":"string"},"avatar_url":{"type":"null"}},"required":["id","first_name","last_name","first_name_ar","last_name_ar","email","mobile","language_pref","avatar_url"]}},"required":["token","member"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/payments/{payment}/status":{"get":{"operationId":"publicJoin.status","summary":"Public payment status for the join-return screen \u2014 the prospect isn't logged in yet, so this\nexposes only the status of a payment (by its unguessable ULID), brand-scoped. Lets the return\npage poll until the webhook settles (\u2192 membership active) or the charge fails","tags":["Public (no auth)"],"parameters":[{"name":"payment","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"paid":{"type":"boolean"}},"required":["status","paid"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/public/corporate/preflight":{"post":{"operationId":"publicCorporate.preflight","tags":["Public (no auth)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":60}},"required":["code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"valid":{"type":"boolean"},"company":{"type":"string"},"company_ar":{"type":"string"},"discount_percent":{"type":"integer"},"requires_employee_id":{"type":"boolean"},"seats_remaining":{"type":["object","null"]},"plan":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"duration_months":{"type":"string"}},"required":["id","name","name_ar","duration_months"]}},"required":["valid","company","company_ar","discount_percent","requires_employee_id","seats_remaining","plan"]},{"type":"object","properties":{"valid":{"type":"boolean"},"reason":{"anyOf":[{"type":"string"},{"type":"string","enum":["invalid_code"]}]}},"required":["valid","reason"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/corporate/join":{"post":{"operationId":"publicCorporate.join","summary":"Redeem a code and self-enroll: persist the Employee-ID doc, draft the discounted B2B contract,\ncapture a PENDING payment, open the two-step KYC, and return the Tap hosted-checkout redirect","tags":["Public (no auth)"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"code":{"type":"string","maxLength":60},"first_name":{"type":"string","maxLength":120},"last_name":{"type":["string","null"],"maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"gender":{"$ref":"#/components/schemas/Gender"},"national_id":{"type":["string","null"]},"iqama_number":{"type":["string","null"],"maxLength":20},"mobile":{"type":["string","null"],"maxLength":20},"email":{"type":["string","null"],"format":"email","maxLength":160},"nationality":{"type":["string","null"],"maxLength":60},"dob":{"type":["string","null"],"format":"date-time"},"employee_id":{"type":["string","null"],"maxLength":60},"club_id":{"type":"string"},"employee_id_document":{"type":["string","null"],"format":"binary","contentMediaType":"application/octet-stream","description":"Conditionally required: the service rejects a missing file when the account requires it.","maxLength":8192},"pdpl_consent":{"type":["boolean","null"]},"password":{"type":["string","null"],"description":"Optional self-set login password \u2014 used only when the member has none yet.","minLength":8,"maxLength":72}},"required":["code","first_name","gender","club_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"employee_id":{"type":"string"},"contract_id":{"type":"string"},"payment_id":{"type":"string"},"amount_minor":{"type":["object","null"]},"currency":{"type":"string"},"status":{"type":"string","const":"pending_employment"},"redirect_url":{"type":["string","null"]}},"required":["member_id","employee_id","contract_id","payment_id","amount_minor","currency","status","redirect_url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/corporate/register":{"post":{"operationId":"publicCorporate.register","summary":"Company partnership enquiry (self-service B2B registration). A company registers its interest from\nthe public site; we persist it as a prospect lead for the gym's sales team \u2014 no account, seats, or\npayment yet (those are negotiated). Returns a simple acknowledgement","tags":["Public (no auth)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"company_name":{"type":"string","maxLength":160},"company_name_ar":{"type":["string","null"],"maxLength":160},"cr_number":{"type":["string","null"],"maxLength":60},"contact_name":{"type":"string","maxLength":120},"contact_email":{"type":"string","format":"email","maxLength":160},"contact_phone":{"type":["string","null"],"maxLength":30},"est_headcount":{"type":["integer","null"],"minimum":1,"maximum":1000000},"message":{"type":["string","null"],"maxLength":2000}},"required":["company_name","contact_name","contact_email"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"received":{"type":"boolean"},"lead_id":{"type":"string"},"company_name":{"type":"string"}},"required":["received","lead_id","company_name"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/subscription-tiers":{"get":{"operationId":"gymOnboarding.tiers","summary":"GET /public/subscription-tiers \u2014 the public plans shown on the Memvera landing","tags":["Public (no auth)"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tiers":{"type":"array","items":{"type":"object","properties":{"tier":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"tagline":{"type":["string","null"]},"monthly_price_minor":{"type":"integer"},"currency":{"type":"string"},"max_clubs":{"type":["integer","null"]},"max_members":{"type":["integer","null"]},"modules":{"type":"array","items":{}},"features":{"description":"Prefer the curated marketing copy for the default tiers; otherwise\nderive the bullet list from the plan's included module names.","anyOf":[{"type":"string"},{"type":"array","items":{}}]}},"required":["tier","name","name_ar","tagline","monthly_price_minor","currency","max_clubs","max_members","modules","features"]}},"modules":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"group":{"type":["string","null"]}},"required":["key","name","name_ar","group"]}}},"required":["tiers","modules"]}}}}}}},"/public/demo-request":{"post":{"operationId":"demoRequest.store","tags":["Public (no auth)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"company":{"type":"string","maxLength":160},"email":{"type":"string","format":"email","maxLength":160},"phone":{"type":["string","null"],"maxLength":40},"city":{"type":["string","null"],"maxLength":80},"branches":{"type":["string","null"],"maxLength":40},"message":{"type":["string","null"],"maxLength":2000}},"required":["name","company","email"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/gym-signup":{"post":{"operationId":"gymOnboarding.signup","summary":"POST /public/gym-signup \u2014 create a pending signup and open the Tap hosted checkout","tags":["Public (no auth)"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"gym_name":{"type":"string","maxLength":120},"gym_name_ar":{"type":["string","null"],"maxLength":120},"admin_name":{"type":"string","maxLength":120},"admin_email":{"type":"string","format":"email","maxLength":190},"admin_password":{"type":"string","minLength":8},"admin_mobile":{"type":["string","null"],"maxLength":30},"tier":{"type":"string"}},"required":["gym_name","admin_name","admin_email","admin_password","tier"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"signup_id":{"type":"string"},"redirect_url":{"type":["string","null"]},"amount_minor":{"type":"integer"},"currency":{"type":"string","const":"SAR"}},"required":["signup_id","redirect_url","amount_minor","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"}}}},"/public/gym-signup/{signup}/status":{"get":{"operationId":"gymOnboarding.status","summary":"GET /public/gym-signup/{signup}/status \u2014 poll + provision if the charge has settled","tags":["Public (no auth)"],"parameters":[{"name":"signup","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"brand_code":{"type":["string","null"]},"staff_url":{"type":"string"}},"required":["status","brand_code","staff_url"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/public/gym-signup/webhook/{gateway}":{"post":{"operationId":"gymOnboarding.webhook_0","summary":"POST /public/gym-signup/webhook/{gateway} \u2014 Tap callback \u2192 provision on settle","tags":["Public (no auth)"],"parameters":[{"name":"gateway","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}}}},"get":{"operationId":"gymOnboarding.webhook_1","summary":"POST /public/gym-signup/webhook/{gateway} \u2014 Tap callback \u2192 provision on settle","tags":["Public (no auth)"],"parameters":[{"name":"gateway","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}}}}},"/public/offer/{token}":{"get":{"operationId":"campaignTracking.offer","summary":"GET /public/offer/{token} \u2014 the offer behind a campaign email: the recipient's campaign + its\ncoupon (localised fields), used by the member-app /offer/{token} landing page. Reading it counts\nas a click. Cross-brand by design (the token is the credential), so brand scopes are bypassed","tags":["Public (no auth)"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"member_first_name":{},"brand_name":{"type":"string"},"campaign_name":{"type":"string"},"message":{"type":["string","null"]},"offer":{"type":["object","null"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":"string"},"discount_type":{"type":"string"},"amount":{"type":"string"},"max_discount_minor":{"type":"string"},"min_subtotal_minor":{"type":"string"},"valid_to":{"type":"string"},"scope":{"type":"string"},"ended_reason":{"type":["string","null"],"description":"Whether the code would actually redeem right now. Deactivating a coupon is the\nkill-switch for a campaign already in the wild, so the landing page must say the\noffer ended instead of showing a code the till will reject.","enum":["exhausted","expired","not_started","inactive",null]},"is_redeemable":{"type":"boolean"}},"required":["code","name","name_ar","discount_type","amount","max_discount_minor","min_subtotal_minor","valid_to","scope","ended_reason","is_redeemable"]}},"required":["member_first_name","brand_name","campaign_name","message","offer"]}}}},"404":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"Offer not found."}},"required":["message"]}}}}}}},"/kpi/contract-viability":{"get":{"operationId":"membershipKpi.contractViability","summary":"Contract Viability KPI (RFP 01 #14). Brand-scoped portfolio-health snapshot:\nactive volume, near-term expiries, frozen count, average contract value, and the\nchurn/renewal ratios. Counts are computed with brand-scoped Contract queries","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"active_contracts":{"type":"integer","minimum":0},"expiring_30d":{"type":"integer","minimum":0},"frozen_count":{"type":"integer","minimum":0},"avg_contract_value_minor":{"type":"integer"},"churn_rate":{"type":"number"},"renewal_rate":{"type":"number"}},"required":["active_contracts","expiring_30d","frozen_count","avg_contract_value_minor","churn_rate","renewal_rate"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/validation-pending":{"get":{"operationId":"validationPending.summary","summary":"GET /reports/validation-pending \u2014 the exception counts (KPI tiles)","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"summary":{"type":"object","properties":{"unsigned_agreement":{"type":"integer","minimum":0},"unpaid":{"type":"integer","minimum":0},"pending_contract":{"type":"integer","minimum":0},"pending_cancellation":{"type":"integer","minimum":0},"pending_approval":{"type":"integer","minimum":0},"pending_document":{"type":"integer","minimum":0},"missing_waiver":{"type":"integer","minimum":0}},"required":["unsigned_agreement","unpaid","pending_contract","pending_cancellation","pending_approval","pending_document","missing_waiver"]}},"required":["summary"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/validation-pending/items":{"get":{"operationId":"validationPending.items","summary":"GET /reports/validation-pending/items?type=&club_id=&q=&page= \u2014 paginated items for one category","tags":["Reports & Analytics"],"parameters":[{"name":"type","in":"query","required":true,"schema":{"type":"string","enum":["unsigned_agreement","unpaid","pending_contract","pending_cancellation","pending_approval","pending_document","missing_waiver"]}},{"name":"club_id","in":"query","schema":{"type":["string","null"],"maxLength":40}},{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":120}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"],"minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"},"priority":{"anyOf":[{"type":"string"},{"type":"string","enum":["medium"]}]},"ref_id":{"type":"string"},"member_id":{"type":["string","null"]},"member_name":{"type":["string","null"]},"club_id":{"type":["string","null"]},"club_name":{"type":["string","null"]},"detail":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["type","priority","ref_id","member_id","member_name","club_id","club_name","detail","created_at"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"integer"},"last_page":{"type":"integer"}},"required":["page","per_page","total","last_page"]}},"required":["data","meta"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/bi/dashboards":{"get":{"operationId":"biEmbed.index","summary":"GET /manage/bi/dashboards \u2014 the embeddable dashboards (key + title) for the Insights tabs","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"string"},"dashboards":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"title":{"type":"string"}},"required":["key","title"]}}},"required":["configured","dashboards"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/bi/embed":{"get":{"operationId":"biEmbed.embed","summary":"GET /manage/bi/embed?dashboard=<key> \u2014 a short-lived signed embed URL, Brand locked to the user's gym","tags":["Reports & Analytics"],"parameters":[{"name":"dashboard","in":"query","required":true,"schema":{"type":"string"}},{"name":"lang","in":"query","schema":{"type":"string","enum":["ar","en"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"503":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"BI embedding is not configured."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/membership-by-plan":{"get":{"operationId":"analytics.membershipByPlan","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"membership_plan_id":{"type":["string","null"]},"plan":{"type":"string"},"active_contracts":{"type":"integer"}},"required":["membership_plan_id","plan","active_contracts"]}}},"required":["rows"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/revenue-by-month":{"get":{"operationId":"analytics.revenueByMonth","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"month":{"type":"string"},"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["month","net_minor","vat_minor","gross_minor"]}}},"required":["rows"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/branch-achievement":{"get":{"operationId":"branchAchievement.index","tags":["Reports & Analytics"],"parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"branch":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"from":{"type":"string"},"to":{"type":"string"},"currency":{"type":"string","const":"SAR"},"sales":{"type":"object","properties":{"new":{"type":"object","properties":{"count":{"type":"string"},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]},"renewal":{"type":"object","properties":{"count":{"type":"string"},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]},"total":{"type":"object","properties":{"count":{"type":"string"},"amount_minor":{"type":"string"}},"required":["count","amount_minor"]},"target":{"type":"object","properties":{"count":{"type":"integer"},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]},"achieved_count_pct":{"type":["number","null"]},"achieved_amount_pct":{"type":["number","null"]}},"required":["new","renewal","total","target","achieved_count_pct","achieved_amount_pct"]},"renewal":{"type":"object","properties":{"expiring":{"type":"integer","minimum":0},"target":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"renewed":{"type":"integer","minimum":0},"achieved_pct":{"type":["number","null"]}},"required":["expiring","target","renewed","achieved_pct"]},"sales_mix":{"type":"object","properties":{"pt":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]},"drop_in":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]}},"required":["pt","drop_in"]},"returning":{"type":"integer","minimum":0},"active_pt":{"type":"integer"},"durations":{"type":"object","properties":{"new":{"type":"array","items":{}},"renewal":{"type":"array","items":{}}},"required":["new","renewal"]},"free_trials":{"type":"object","properties":{"booked":{"type":"integer","minimum":0},"showed":{"type":"integer","minimum":0},"no_show":{"type":"integer","minimum":0},"cancelled":{"type":"integer","minimum":0},"converted":{"type":"integer","minimum":0},"conversion_pct":{"type":["number","null"]}},"required":["booked","showed","no_show","cancelled","converted","conversion_pct"]},"accounts":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"count":{"type":"string"}},"required":["source","count"]}},"retail_pt":{"type":"object","properties":{"retail":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]},"addons":{"type":"object","properties":{"count":{"type":"integer","minimum":0},"amount_minor":{"type":"integer"}},"required":["count","amount_minor"]}},"required":["retail","addons"]},"calls":{"type":"object","properties":{"total":{"type":"integer","minimum":0},"outbound":{"type":"integer","minimum":0},"inbound":{"type":"integer","minimum":0},"answered":{"type":"integer","minimum":0},"no_answer":{"type":"integer","minimum":0},"busy":{"type":"integer","minimum":0},"other":{"type":"string"},"talk_sec":{"type":"integer"}},"required":["total","outbound","inbound","answered","no_answer","busy","other","talk_sec"]}},"required":["branch","from","to","currency","sales","renewal","sales_mix","returning","active_pt","durations","free_trials","accounts","retail_pt","calls"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/branch-dashboard":{"get":{"operationId":"branchReports.dashboard","summary":"GET /reports/branch-dashboard","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"branch":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"from":{"type":"string"},"to":{"type":"string"},"currency":{"type":"string","const":"SAR"},"sales":{"type":"object","properties":{"new_count":{"type":"string"},"renewal_count":{"type":"string"},"addon_count":{"type":"integer","minimum":0},"pt_count":{"type":"integer","minimum":0},"pt_amount_minor":{"type":"integer"},"drop_in_count":{"type":"integer","minimum":0},"drop_in_amount_minor":{"type":"integer"},"total_count":{"type":"string"},"total_amount_minor":{"type":"string"},"target_amount_minor":{"type":"integer"},"achieved_pct":{"type":["number","null"]}},"required":["new_count","renewal_count","addon_count","pt_count","pt_amount_minor","drop_in_count","drop_in_amount_minor","total_count","total_amount_minor","target_amount_minor","achieved_pct"]},"renewal":{"type":"object","properties":{"expiring":{"type":"integer","minimum":0},"target":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"renewed":{"type":"integer","minimum":0},"achieved_pct":{"type":["number","null"]}},"required":["expiring","target","renewed","achieved_pct"]},"members":{"type":"object","properties":{"active":{"type":"integer","minimum":0},"expired":{"type":"integer","minimum":0},"new_accounts":{"type":"integer"},"returning":{"type":"integer","minimum":0},"active_pt":{"type":"integer","minimum":0}},"required":["active","expired","new_accounts","returning","active_pt"]},"activity":{"type":"object","properties":{"signed_in":{"type":"integer","minimum":0},"conducted":{"type":"string"},"no_show":{"type":"integer"},"cancelled":{"type":"integer"},"walk_ins":{"type":"integer"}},"required":["signed_in","conducted","no_show","cancelled","walk_ins"]},"calls":{"type":"object","properties":{"total":{"type":"integer","minimum":0},"outbound":{"type":"integer","minimum":0},"inbound":{"type":"integer","minimum":0},"answered":{"type":"integer","minimum":0},"no_answer":{"type":"integer","minimum":0},"busy":{"type":"integer","minimum":0},"other":{"type":"string"},"talk_sec":{"type":"integer"}},"required":["total","outbound","inbound","answered","no_answer","busy","other","talk_sec"]},"free_trials":{"type":"object","properties":{"booked":{"type":"integer","minimum":0},"showed":{"type":"integer","minimum":0},"no_show":{"type":"integer","minimum":0},"cancelled":{"type":"integer","minimum":0},"converted":{"type":"integer","minimum":0},"conversion_pct":{"type":["number","null"]}},"required":["booked","showed","no_show","cancelled","converted","conversion_pct"]},"durations":{"type":"object","properties":{"new":{"type":"array","items":{}},"renewal":{"type":"array","items":{}}},"required":["new","renewal"]},"attendance_by_category":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"count":{"type":"integer"}},"required":["category","count"]}}},"required":["branch","from","to","currency","sales","renewal","members","activity","calls","free_trials","durations","attendance_by_category"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/individual-achievement":{"get":{"operationId":"branchReports.individual","summary":"GET /reports/individual-achievement","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"branch":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"from":{"type":"string"},"to":{"type":"string"},"currency":{"type":"string","const":"SAR"},"reps":{"type":"array","items":{"type":"object","properties":{"rep":{"type":"string"},"accounts":{"type":"integer"},"account_types":{"type":"string"},"calls":{"type":"integer"},"calls_answered":{"type":"integer"},"calls_busy":{"type":"integer"},"calls_no_answer":{"type":"integer"},"talk_min":{"type":"integer"},"tours":{"type":"string"},"tours_showed":{"type":"string"},"ft_sales":{"type":"string"},"new_count":{"type":"string"},"new_amount_minor":{"type":"integer"},"renewal_count":{"type":"string"},"renewal_amount_minor":{"type":"integer"},"pt_count":{"type":"integer"},"addon_count":{"type":"string"},"addon_amount_minor":{"type":"integer"},"total_amount_minor":{"type":"string"}},"required":["rep","accounts","account_types","calls","calls_answered","calls_busy","calls_no_answer","talk_min","tours","tours_showed","ft_sales","new_count","new_amount_minor","renewal_count","renewal_amount_minor","pt_count","addon_count","addon_amount_minor","total_amount_minor"]}},"totals":{"type":"object","properties":{"accounts":{"anyOf":[{"type":"integer"},{"type":"number"}]},"calls":{"anyOf":[{"type":"integer"},{"type":"number"}]},"calls_answered":{"anyOf":[{"type":"integer"},{"type":"number"}]},"calls_busy":{"anyOf":[{"type":"integer"},{"type":"number"}]},"calls_no_answer":{"anyOf":[{"type":"integer"},{"type":"number"}]},"talk_min":{"anyOf":[{"type":"integer"},{"type":"number"}]},"tours":{"anyOf":[{"type":"integer"},{"type":"number"}]},"ft_sales":{"anyOf":[{"type":"integer"},{"type":"number"}]},"new_count":{"anyOf":[{"type":"integer"},{"type":"number"}]},"renewal_count":{"anyOf":[{"type":"integer"},{"type":"number"}]},"pt_count":{"anyOf":[{"type":"integer"},{"type":"number"}]},"total_amount_minor":{"anyOf":[{"type":"integer"},{"type":"number"}]}},"required":["accounts","calls","calls_answered","calls_busy","calls_no_answer","talk_min","tours","ft_sales","new_count","renewal_count","pt_count","total_amount_minor"]}},"required":["branch","from","to","currency","reps","totals"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/branch-target":{"get":{"operationId":"branchReports.targetShow","summary":"GET /reports/branch-target?club_id&month \u2014 the branch's target for a month (or nulls)","tags":["Reports & Analytics"],"parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"month","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"month":{"type":"string"},"new_count":{"anyOf":[{"type":"string"},{"type":"integer","enum":[0]}]},"renewal_count":{"anyOf":[{"type":"string"},{"type":"integer","enum":[0]}]},"amount_minor":{"anyOf":[{"type":"string"},{"type":"integer","enum":[0]}]}},"required":["club_id","month","new_count","renewal_count","amount_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"branchReports.targetSave","summary":"PUT /reports/branch-target \u2014 upsert the branch's target for a month (config.manage)","tags":["Reports & Analytics"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"club_id":{"type":"string"},"month":{"type":"string","format":"date-time"},"new_count":{"type":"integer","minimum":0},"renewal_count":{"type":"integer","minimum":0},"amount_minor":{"type":"integer","minimum":0}},"required":["club_id","month","new_count","renewal_count","amount_minor"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"saved":{"type":"boolean"},"target":{"$ref":"#/components/schemas/BranchTarget"}},"required":["saved","target"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/supervisor-commission":{"get":{"operationId":"roleCommission.previewSupervisor","summary":"Preview a supervisor's commission for a branch + period","tags":["Reports & Analytics"],"parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"employee_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"renewal_rate","in":"query","schema":{"type":["number","null"],"minimum":0,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"branch":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"supervisor":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"currency":{"type":"string","const":"SAR"},"enabled":{"type":"boolean"},"renewal_rate_pct":{"type":"number"},"renewal_rate_source":{"type":"string","enum":["manual","computed"]},"expiring_pool":{"type":"integer","minimum":0},"components":{"type":"object","properties":{"personal":{"type":"object","properties":{"new_count":{"type":"integer","minimum":0},"gate_new_sales":{"type":"integer"},"gate_met":{"type":"boolean"},"base_amount_minor":{"type":"string"},"rate_pct":{"type":"number"},"commission_minor":{"type":"integer"}},"required":["new_count","gate_new_sales","gate_met","base_amount_minor","rate_pct","commission_minor"]},"branch":{"type":"object","properties":{"new_count":{"type":"integer","minimum":0},"gate_new_sales":{"type":"integer"},"gate_renewal_rate":{"type":"number"},"gate_met":{"type":"string"},"base_amount_minor":{"type":"string"},"rate_pct":{"type":"number"},"commission_minor":{"type":"integer"}},"required":["new_count","gate_new_sales","gate_renewal_rate","gate_met","base_amount_minor","rate_pct","commission_minor"]},"renewal":{"type":"object","properties":{"rate_pct":{"type":"string"},"band1_min_rate":{"type":"number"},"band2_min_rate":{"type":"number"},"base_amount_minor":{"type":"string"},"rate_pct_applied":{"type":"number"},"commission_minor":{"type":"integer"}},"required":["rate_pct","band1_min_rate","band2_min_rate","base_amount_minor","rate_pct_applied","commission_minor"]}},"required":["personal","branch","renewal"]},"total_commission_minor":{"type":"string"}},"required":["branch","supervisor","from","to","currency","enabled","renewal_rate_pct","renewal_rate_source","expiring_pool","components","total_commission_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/coach-commission":{"get":{"operationId":"roleCommission.previewCoach","summary":"Preview a coach's commission for a period","tags":["Reports & Analytics"],"parameters":[{"name":"employee_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"coach":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"currency":{"type":"string","const":"SAR"},"enabled":{"type":"boolean"},"pt":{"type":"object","properties":{"attended":{"type":"string"},"no_show":{"type":"integer"},"attended_amount":{"type":"integer"},"noshow_amount":{"type":"integer"},"commission_minor":{"type":"string"}},"required":["attended","no_show","attended_amount","noshow_amount","commission_minor"]},"free_trials":{"type":"object","properties":{"hosted":{"type":"integer","minimum":0},"same_day_conversions":{"type":"integer"},"within_window_conversions":{"type":"integer"},"within_days":{"type":"integer"},"same_day_amount":{"type":"integer"},"within_amount":{"type":"integer"},"commission_minor":{"type":"string"}},"required":["hosted","same_day_conversions","within_window_conversions","within_days","same_day_amount","within_amount","commission_minor"]},"total_commission_minor":{"type":"string"}},"required":["coach","from","to","currency","enabled","pt","free_trials","total_commission_minor"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/commission-by-trainer":{"get":{"operationId":"analytics.commissionByTrainer","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"employee_id":{"type":["string","null"]},"trainer":{"type":"string"},"amount_minor":{"type":"integer"}},"required":["employee_id","trainer","amount_minor"]}}},"required":["rows"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/sales-commission":{"get":{"operationId":"salesCommission.report","summary":"GET /reports/sales-commission?from&to \u2014 per-rep sales + commission (earned/paid/unpaid)","tags":["Reports & Analytics"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"string"},"total_sales_minor":{"type":"integer"},"total_deals":{"type":"integer","minimum":0},"total_minor":{"type":"integer"},"total_paid_minor":{"type":"integer"},"total_unpaid_minor":{"type":"integer"},"currency":{"type":"string","const":"SAR"}},"required":["rows","total_sales_minor","total_deals","total_minor","total_paid_minor","total_unpaid_minor","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/sales-commission/export":{"get":{"operationId":"salesCommission.reportExport","summary":"GET /reports/sales-commission/export \u2014 the per-rep report as a CSV","tags":["Reports & Analytics"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"text/csv; charset=UTF-8":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string","const":"attachment; filename=\"sales-commission-report.csv\""}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/sales-commission/detail":{"get":{"operationId":"salesCommission.reportDetail","summary":"GET /reports/sales-commission/detail?employee&from&to \u2014 a rep's individual commission rows","tags":["Reports & Analytics"],"parameters":[{"name":"employee","in":"query","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"source":{"type":"string"},"contract_id":{"type":"string"},"amount_minor":{"type":"integer"},"paid":{"type":"boolean"},"posted_at":{"type":"string"}},"required":["source","contract_id","amount_minor","paid","posted_at"]}}},"required":["rows"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/sales-leaderboard":{"get":{"operationId":"salesCommission.leaderboard","summary":"GET /reports/sales-leaderboard?from&to \u2014 top sales people ranked by sales value, with commission","tags":["Reports & Analytics"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"string"},"total_sales_minor":{"type":"integer"},"total_deals":{"type":"integer","minimum":0},"total_commission_minor":{"type":"integer","description":"Column totals for the table footer."},"total_deal_target":{"type":"integer"},"total_sales_target_minor":{"type":"integer"},"currency":{"type":"string","const":"SAR"}},"required":["rows","total_sales_minor","total_deals","total_commission_minor","total_deal_target","total_sales_target_minor","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/sales-leaderboard/detail":{"get":{"operationId":"salesCommission.leaderboardDetail","summary":"GET /reports/sales-leaderboard/detail?employee&from&to \u2014 the individual deals behind a rep's row","tags":["Reports & Analytics"],"parameters":[{"name":"employee","in":"query","required":true,"schema":{"type":"string"}},{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"contract_id":{"type":"string"},"member_id":{"type":"string"},"member_name":{"type":"string"},"member_name_ar":{"type":"string"},"plan_name":{"type":"string"},"type":{"type":"string","enum":["renew","new"]},"sale_date":{"type":"string"},"amount_minor":{"type":"integer"},"commission_minor":{"type":"integer"}},"required":["contract_id","member_id","member_name","member_name_ar","plan_name","type","sale_date","amount_minor","commission_minor"]}}},"required":["rows"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/custom/columns":{"get":{"operationId":"customReport.columns","summary":"GET /reports/custom/columns \u2014 the selectable columns + filter keys for the builder","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"columns":{"type":"array","items":{}},"filters":{"type":"array","prefixItems":[{"type":"string","const":"q"},{"type":"string","const":"gender"},{"type":"string","const":"value_segment"},{"type":"string","const":"home_club_id"},{"type":"string","const":"membership"},{"type":"string","const":"joined_from"},{"type":"string","const":"joined_to"},{"type":"string","const":"churn_min"}],"minItems":8,"maxItems":8,"additionalItems":false}},"required":["columns","filters"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/custom/run":{"post":{"operationId":"customReport.run","summary":"POST /reports/custom/run \u2014 preview the report (capped)","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"columns":{"type":"array","items":{}},"rows":{"type":"array","items":{"type":"array","items":{}}},"total":{"type":"integer"}},"required":["columns","rows","total"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/custom/export":{"post":{"operationId":"customReport.export","summary":"POST /reports/custom/export?format=xlsx|pdf \u2014 download the full report","tags":["Reports & Analytics"],"parameters":[{"name":"format","in":"query","schema":{"type":"string","default":"xlsx"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}},"application/pdf":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/custom/templates":{"get":{"operationId":"customReport.templates","summary":"GET /reports/custom/templates \u2014 this brand's saved report templates","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"columns":{"type":"array","items":{}},"filters":{"type":"array","items":{}}},"required":["id","name","columns","filters"]}}},"required":["templates"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"customReport.saveTemplate","summary":"POST /reports/custom/templates \u2014 save (upsert by name) a template","tags":["Reports & Analytics"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"filters":{"type":["array","null"],"items":{"type":"string"}},"columns":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["name","columns"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/custom/templates/{template}":{"delete":{"operationId":"customReport.deleteTemplate","summary":"DELETE /reports/custom/templates/{template}","tags":["Reports & Analytics"],"parameters":[{"name":"template","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/retail-top":{"get":{"operationId":"analytics.retailTop","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":["string","null"]},"name":{"type":"string"},"quantity":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["product_id","name","quantity","gross_minor"]}}},"required":["rows"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/sales":{"get":{"operationId":"salesReport.index","summary":"GET /reports/sales \u2014 aggregated rows + totals for the selected dimension + date range","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"group_by":{"anyOf":[{"type":"string"},{"type":"string","enum":["branch"]}]},"currency":{"type":"string","const":"SAR"},"totals":{"type":"object","properties":{"gross_minor":{"type":"integer"},"count":{"type":"integer"},"membership_minor":{"type":"integer"},"retail_minor":{"type":"integer"},"addon_minor":{"type":"integer"},"membership_new_minor":{"type":"integer"},"membership_renewal_minor":{"type":"integer"},"refunds_minor":{"type":"integer"},"discounts_minor":{"type":"integer"},"net_minor":{"type":"string"},"avg_minor":{"type":"integer"}},"required":["gross_minor","count","membership_minor","retail_minor","addon_minor","membership_new_minor","membership_renewal_minor","refunds_minor","discounts_minor","net_minor","avg_minor"]},"compare":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"totals":{"type":"object","properties":{"gross_minor":{"type":"integer"},"count":{"type":"integer"},"membership_minor":{"type":"integer"},"retail_minor":{"type":"integer"},"addon_minor":{"type":"integer"},"membership_new_minor":{"type":"integer"},"membership_renewal_minor":{"type":"integer"},"refunds_minor":{"type":"integer"},"discounts_minor":{"type":"integer"},"net_minor":{"type":"string"},"avg_minor":{"type":"integer"}},"required":["gross_minor","count","membership_minor","retail_minor","addon_minor","membership_new_minor","membership_renewal_minor","refunds_minor","discounts_minor","net_minor","avg_minor"]},"deltas":{"type":"object","properties":{"gross_pct":{"type":["number","null"]},"net_pct":{"type":["number","null"]},"count_pct":{"type":["number","null"]},"avg_pct":{"type":["number","null"]},"refunds_pct":{"type":["number","null"]},"discounts_pct":{"type":["number","null"]}},"required":["gross_pct","net_pct","count_pct","avg_pct","refunds_pct","discounts_pct"]}},"required":["from","to","totals","deltas"]},"series":{"type":"object","properties":{"granularity":{"type":"string","enum":["day","week","month"]},"points":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"gross_minor":{"type":"string"},"count":{"type":"string"}},"required":["label","gross_minor","count"]}}},"required":["granularity","points"]},"by_method":{"type":"array","items":{"type":"object","properties":{"method":{"type":"string"},"gross_minor":{"type":"string"},"count":{"type":"string"}},"required":["method","gross_minor","count"]}},"rows":{"type":"array","items":{}}},"required":["from","to","group_by","currency","totals","compare","series","by_method","rows"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/sales/export":{"get":{"operationId":"salesReport.export","summary":"GET /reports/sales/export \u2014 the same report as a CSV download","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"text/csv; charset=UTF-8":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sales/transactions":{"get":{"operationId":"salesTransactions.index","summary":"GET /sales/transactions \u2014 the unified, filtered, paginated transaction list","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{}},"total":{"type":"integer"},"page":{"type":["object","null"]},"per_page":{"type":"integer","const":25},"last_page":{"type":["object","null"]},"gross_minor":{"anyOf":[{"type":"integer"},{"type":"number"}]},"from":{"type":"string"},"to":{"type":"string"},"currency":{"type":"string","const":"SAR"}},"required":["data","total","page","per_page","last_page","gross_minor","from","to","currency"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sales/transactions/export":{"get":{"operationId":"salesTransactions.export","summary":"GET /sales/transactions/export \u2014 the filtered list as a CSV download","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"text/csv; charset=UTF-8":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string","const":"attachment; filename=\"sales-transactions.csv\""}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sales/transactions/{type}/{id}":{"get":{"operationId":"salesTransactions.show","summary":"GET /sales/transactions/{type}/{id} \u2014 full detail for one order","tags":["Reports & Analytics"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"membership"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"anyOf":[{"type":"string"},{"type":"string","enum":["pos"]}]},"lines":{"type":"array","prefixItems":[{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"string","enum":["Membership"]}]},"quantity":{"type":"integer","const":1},"unit_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["name","quantity","unit_minor","gross_minor"]}],"minItems":1,"maxItems":1,"additionalItems":false},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"anyOf":[{"type":"string"},{"type":"string","enum":["paid"]}]},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_seller":{"type":"boolean"},"editable_payment":{"type":"boolean"},"sold_by_employee_id":{"type":"string"},"sold_by_name":{"type":["string","null"]},"assist_employee_id":{"type":"string"},"assist_name":{"type":["string","null"]}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_seller","editable_payment","sold_by_employee_id","sold_by_name","assist_employee_id","assist_name"]},{"type":"null"},{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"retail"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"type":"string","const":"pos"},"lines":{"type":"string"},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"anyOf":[{"type":"string"},{"type":"string","enum":["paid"]}]},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_payment":{"type":"boolean"}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_payment"]},{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"addon"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"type":"string","const":"pos"},"lines":{"type":"array","prefixItems":[{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"string","enum":["Add-on"]}]},"quantity":{"type":"integer"},"unit_minor":{"type":"integer"},"gross_minor":{"type":"string"}},"required":["name","quantity","unit_minor","gross_minor"]}],"minItems":1,"maxItems":1,"additionalItems":false},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"string"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"type":"string","const":"paid"},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_seller":{"type":"boolean"},"editable_payment":{"type":"boolean"},"sold_by_employee_id":{"type":"string"},"sold_by_name":{"type":["string","null"]}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_seller","editable_payment","sold_by_employee_id","sold_by_name"]}]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sales/transactions/{type}/{id}/seller":{"patch":{"operationId":"salesTransactions.updateSeller","summary":"PATCH /sales/transactions/{type}/{id}/seller \u2014 re-attribute a membership or add-on sale to a rep","tags":["Reports & Analytics"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"sold_by_employee_id":{"type":["string","null"]},"assist_employee_id":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"membership"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"anyOf":[{"type":"string"},{"type":"string","enum":["pos"]}]},"lines":{"type":"array","prefixItems":[{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"string","enum":["Membership"]}]},"quantity":{"type":"integer","const":1},"unit_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["name","quantity","unit_minor","gross_minor"]}],"minItems":1,"maxItems":1,"additionalItems":false},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"anyOf":[{"type":"string"},{"type":"string","enum":["paid"]}]},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_seller":{"type":"boolean"},"editable_payment":{"type":"boolean"},"sold_by_employee_id":{"type":"string"},"sold_by_name":{"type":["string","null"]},"assist_employee_id":{"type":"string"},"assist_name":{"type":["string","null"]}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_seller","editable_payment","sold_by_employee_id","sold_by_name","assist_employee_id","assist_name"]},{"type":"null"},{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"retail"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"type":"string","const":"pos"},"lines":{"type":"string"},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"anyOf":[{"type":"string"},{"type":"string","enum":["paid"]}]},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_payment":{"type":"boolean"}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_payment"]},{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"addon"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"type":"string","const":"pos"},"lines":{"type":"array","prefixItems":[{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"string","enum":["Add-on"]}]},"quantity":{"type":"integer"},"unit_minor":{"type":"integer"},"gross_minor":{"type":"string"}},"required":["name","quantity","unit_minor","gross_minor"]}],"minItems":1,"maxItems":1,"additionalItems":false},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"string"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"type":"string","const":"paid"},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_seller":{"type":"boolean"},"editable_payment":{"type":"boolean"},"sold_by_employee_id":{"type":"string"},"sold_by_name":{"type":["string","null"]}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_seller","editable_payment","sold_by_employee_id","sold_by_name"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/sales/transactions/{type}/{id}/payment-method":{"patch":{"operationId":"salesTransactions.updatePaymentMethod","summary":"PATCH /sales/transactions/{type}/{id}/payment-method \u2014 set / correct the tender on a transaction","tags":["Reports & Analytics"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"payment_method":{"type":["string","null"],"maxLength":40}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"membership"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"anyOf":[{"type":"string"},{"type":"string","enum":["pos"]}]},"lines":{"type":"array","prefixItems":[{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"string","enum":["Membership"]}]},"quantity":{"type":"integer","const":1},"unit_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["name","quantity","unit_minor","gross_minor"]}],"minItems":1,"maxItems":1,"additionalItems":false},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"anyOf":[{"type":"string"},{"type":"string","enum":["paid"]}]},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_seller":{"type":"boolean"},"editable_payment":{"type":"boolean"},"sold_by_employee_id":{"type":"string"},"sold_by_name":{"type":["string","null"]},"assist_employee_id":{"type":"string"},"assist_name":{"type":["string","null"]}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_seller","editable_payment","sold_by_employee_id","sold_by_name","assist_employee_id","assist_name"]},{"type":"null"},{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"retail"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"type":"string","const":"pos"},"lines":{"type":"string"},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"integer"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"anyOf":[{"type":"string"},{"type":"string","enum":["paid"]}]},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_payment":{"type":"boolean"}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_payment"]},{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","const":"addon"},"reference":{"type":"string"},"date":{"type":"string"},"customer":{"type":"string"},"club_name":{"type":"string"},"channel":{"type":"string","const":"pos"},"lines":{"type":"array","prefixItems":[{"type":"object","properties":{"name":{"anyOf":[{"type":"string"},{"type":"string","enum":["Add-on"]}]},"quantity":{"type":"integer"},"unit_minor":{"type":"integer"},"gross_minor":{"type":"string"}},"required":["name","quantity","unit_minor","gross_minor"]}],"minItems":1,"maxItems":1,"additionalItems":false},"totals":{"type":"object","properties":{"net_minor":{"type":"integer"},"vat_minor":{"type":"integer"},"discount_minor":{"type":"integer"},"gross_minor":{"type":"string"}},"required":["net_minor","vat_minor","discount_minor","gross_minor"]},"status":{"type":"string","const":"paid"},"payment_method":{"type":"string"},"currency":{"type":"string","const":"SAR"},"seller_name":{"description":"ZATCA simplified tax invoice (RFP 03): the seller fields + the base64 TLV QR payload.","anyOf":[{"type":"string"},{"type":"string","enum":["Memvera"]}]},"vat_number":{"type":["string","null"]},"zatca_qr":{"type":"string"},"editable_seller":{"type":"boolean"},"editable_payment":{"type":"boolean"},"sold_by_employee_id":{"type":"string"},"sold_by_name":{"type":["string","null"]}},"required":["id","type","reference","date","customer","club_name","channel","lines","totals","status","payment_method","currency","seller_name","vat_number","zatca_qr","editable_seller","editable_payment","sold_by_employee_id","sold_by_name"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/deferred-revenue":{"get":{"operationId":"deferredRevenueReport.index","tags":["Reports & Analytics"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"page","in":"query","schema":{"type":["integer","null"]}},{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"summary":{"type":"string"},"concentration":{"type":"object","properties":{"by_plan":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"label":{"type":"string"},"deferred_minor":{"type":"integer"}},"required":["label","deferred_minor"]},{"type":"object","properties":{"label":{"type":"string","const":"\u2014"},"deferred_minor":{"type":"integer"}},"required":["label","deferred_minor"]}]}},"by_branch":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"label":{"type":"string"},"deferred_minor":{"type":"integer"}},"required":["label","deferred_minor"]},{"type":"object","properties":{"label":{"type":"string","const":"\u2014"},"deferred_minor":{"type":"integer"}},"required":["label","deferred_minor"]}]}}},"required":["by_plan","by_branch"]},"rollforward":{"type":"object","properties":{"opening_minor":{"type":"integer"},"additions_minor":{"type":"integer"},"recognized_minor":{"type":"integer"},"reversed_minor":{"type":"integer"},"closing_minor":{"type":"string"},"prior":{"type":"object","properties":{"additions_minor":{"type":"integer"},"recognized_minor":{"type":"integer"}},"required":["additions_minor","recognized_minor"]},"deltas":{"type":"object","properties":{"additions_pct":{"type":["number","null"]},"recognized_pct":{"type":["number","null"]}},"required":["additions_pct","recognized_pct"]}},"required":["opening_minor","additions_minor","recognized_minor","reversed_minor","closing_minor","prior","deltas"]},"forecast":{"type":"array","items":{}},"schedules":{"type":"array","items":{}},"page":{"type":["object","null"]},"per_page":{"type":"integer","const":25},"total":{"type":"integer"},"last_page":{"type":["object","null"]},"by_month":{"type":"array","items":{}},"currency":{"type":"string","const":"SAR"}},"required":["from","to","summary","concentration","rollforward","forecast","schedules","page","per_page","total","last_page","by_month","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/deferred-revenue/export":{"get":{"operationId":"deferredRevenueReport.export","tags":["Reports & Analytics"],"parameters":[{"name":"q","in":"query","schema":{"type":["string","null"],"maxLength":100}}],"responses":{"200":{"description":"","content":{"text/csv; charset=UTF-8":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string","const":"attachment; filename=\"deferred-revenue.csv\""}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/deferred-revenue/member":{"get":{"operationId":"deferredRevenueReport.member","summary":"GET /reports/deferred-revenue/member?q= \u2014 search members and roll up each one's deferred schedules","tags":["Reports & Analytics"],"parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"national_id":{"type":["string","null"]},"totals":{"type":"object","properties":{"total_minor":{"type":"integer"},"recognized_minor":{"type":"integer"},"deferred_minor":{"type":"integer"}},"required":["total_minor","recognized_minor","deferred_minor"]},"schedules":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"plan":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"start_date":{"type":"string"},"months":{"type":"integer"},"total_minor":{"type":"integer"},"recognized_minor":{"type":"integer"},"deferred_minor":{"anyOf":[{"type":"null"},{"type":"object"},{"type":"integer","enum":[0]}]},"status":{"type":"string"},"progress":{"type":"integer"},"entries":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"object","properties":{"month":{"type":"string"},"amount_minor":{"type":"integer"},"recognized":{"type":"boolean"}},"required":["month","amount_minor","recognized"]}}]}},"required":["id","plan","start_date","months","total_minor","recognized_minor","deferred_minor","status","progress","entries"]}}},"required":["id","name","name_ar","national_id","totals","schedules"]}},"currency":{"type":"string","const":"SAR"}},"required":["members","currency"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/deferred-revenue/member/{member}/export":{"get":{"operationId":"deferredRevenueReport.memberExport","summary":"GET /reports/deferred-revenue/member/{member}/export \u2014 one member's schedules + monthly lines as CSV","tags":["Reports & Analytics"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"text/csv; charset=UTF-8":{"schema":{"type":"string"}}},"headers":{"Content-Disposition":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/home/summary":{"get":{"operationId":"homeSummary.index","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"today":{"type":"object","properties":{"revenue_minor":{"type":"string"},"new_contracts":{"type":"integer","minimum":0},"check_ins":{"type":"integer","minimum":0}},"required":["revenue_minor","new_contracts","check_ins"]},"attention":{"type":"object","properties":{"pending_approvals":{"type":"integer","minimum":0},"expiring_soon":{"type":"integer","minimum":0},"in_arrears":{"type":"integer","minimum":0}},"required":["pending_approvals","expiring_soon","in_arrears"]},"currency":{"type":"string","const":"SAR"}},"required":["today","attention","currency"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/overview":{"get":{"operationId":"report.overview","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"active_contracts":{"type":"integer","minimum":0},"deferred_revenue_net_minor":{"type":"integer"},"commissions_minor":{"type":"integer"},"sessions_conducted":{"type":"integer","minimum":0}},"required":["active_contracts","deferred_revenue_net_minor","commissions_minor","sessions_conducted"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/series":{"get":{"operationId":"report.series","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"months":{"type":"array","items":{"type":"string"}},"signups":{"type":"array","items":{"type":"integer","minimum":0}},"revenue_minor":{"type":"array","items":{"type":"integer"}},"status":{"type":"string"}},"required":["months","signups","revenue_minor","status"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/reports/wallet-liability":{"get":{"operationId":"wallet.liability","summary":"Total outstanding wallet liability for the brand, with the per-member breakdown (RFP 03 #19)","tags":["Reports & Analytics"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"total_liability_minor":{"type":"integer"},"currency":{"type":"string","const":"SAR"},"members":{"type":"array","items":{"type":"object","properties":{"member_id":{"type":"string"},"name":{"type":["string","null"]},"balance_minor":{"type":"integer"}},"required":["member_id","name","balance_minor"]}}},"required":["total_liability_minor","currency","members"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/bookers":{"get":{"operationId":"scheduling.bookers","summary":"GET /scheduling/bookers \u2014 active employees for the \"booked by\" picker (mirrors the sales sellers list,\nbut permissioned for scheduling staff). Lets the desk attribute a booking to a specific rep/trainer","tags":["Scheduling & Bookings"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"bookers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]}},"required":["id","name","name_ar"]}}},"required":["bookers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions":{"get":{"operationId":"scheduling.sessions","summary":"Sessions in a date window (default: the next 7 days), with booked counts","tags":["Scheduling & Bookings"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"club_id","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"session_type":{"type":"string","description":"Read raw so a stray out-of-enum value (e.g. legacy 'completed') can't 500 the list."},"club":{"type":["string","null"]},"area":{"type":["string","null"]},"trainer":{"type":["string","null"]},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"capacity":{"type":"integer"},"booked":{"type":"integer"},"status":{"type":"string"},"access_type":{"type":"string"}},"required":["id","concept","session_type","club","area","trainer","starts_at","ends_at","capacity","booked","status","access_type"]}}},"required":["sessions"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"scheduling.store","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"concept_id":{"type":"string"},"club_id":{"type":"string"},"area_id":{"type":"string"},"trainer_id":{"type":"string"},"starts_at":{"type":"string","format":"date-time"},"access_type":{"type":"string","description":"RFP 14 #2 \u2014 VIP event: restrict by plan or by single-use access code.","enum":["open","plan","code"]},"code_count":{"type":["integer","null"],"minimum":1,"maximum":200},"access_plan_ids":{"type":["array","null"],"items":{"type":"string"}}},"required":["concept_id","club_id","area_id","trainer_id","starts_at"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"access_type":{"anyOf":[{"type":"string"},{"type":"string","enum":["open"]}]},"codes":{"type":"array","items":{"type":"string"}}},"required":["id","access_type","codes"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/bookings":{"get":{"operationId":"scheduling.allBookings","summary":"GET /scheduling/bookings \u2014 every booking across sessions for one day (front-desk\n\"who's coming in\" view). Table filters: trainer, status, member (name/mobile).\nThe headline counts are date-scoped only \u2014 filtering the table never changes them","tags":["Scheduling & Bookings"],"parameters":[{"name":"date","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"trainer_id","in":"query","schema":{"type":["string","null"]}},{"name":"status","in":"query","schema":{"type":["string","null"],"enum":["booked","waitlisted","confirmed","conducted","no_show","cancelled"]}},{"name":"type","in":"query","schema":{"type":["string","null"],"enum":["gx","pt"]}},{"name":"member","in":"query","schema":{"type":["string","null"],"maxLength":100}},{"name":"page","in":"query","schema":{"type":["integer","null"],"minimum":1}},{"name":"per_page","in":"query","schema":{"type":["integer","null"],"minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"counts":{"type":"object","properties":{"total":{"type":"integer"},"booked":{"type":"integer"},"waitlisted":{"type":"integer"},"attended":{"type":"integer"},"cancelled":{"type":"integer"},"no_show":{"type":"integer"}},"required":["total","booked","waitlisted","attended","cancelled","no_show"]},"bookings":{"type":"string"},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"string"},"last_page":{"type":["object","null"]}},"required":["page","per_page","total","last_page"]}},"required":["counts","bookings","meta"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/schedules":{"get":{"operationId":"scheduling.schedules","summary":"GET /scheduling/schedules \u2014 the timetable as PROGRAMMES: one row per recurring series + one row per\none-off session, within the date window. Series carry cadence + counts; singles carry the occurrence","tags":["Scheduling & Bookings"],"parameters":[{"name":"from","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"to","in":"query","schema":{"type":["string","null"],"format":"date-time"}},{"name":"club_id","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"schedules":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","const":"single"},"id":{"type":"string"},"concept":{"type":"string"},"session_type":{"type":"string"},"club":{"type":["string","null"]},"area":{"type":["string","null"]},"trainer":{"type":["string","null"]},"starts_at":{"type":"string"},"status":{"type":"string"},"sessions_count":{"type":"integer","const":1},"next_at":{"type":"string"}},"required":["type","id","concept","session_type","club","area","trainer","starts_at","status","sessions_count","next_at"]},{"type":"object","properties":{"type":{"type":"string","const":"series"},"id":{"type":"string"},"concept":{"type":"string"},"session_type":{"type":"string"},"club":{},"area":{},"trainer":{"type":["string","null"]},"weekdays":{"type":"string"},"time":{"type":"string"},"starts_on":{"type":"string"},"ends_on":{"type":"string"},"sessions_count":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"upcoming_count":{"anyOf":[{"type":"string"},{"type":"integer","minimum":0}]},"next_at":{"type":"string"}},"required":["type","id","concept","session_type","club","area","trainer","weekdays","time","starts_on","ends_on","sessions_count","upcoming_count","next_at"]}]}}},"required":["schedules"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/series/{series}":{"get":{"operationId":"scheduling.series","summary":"GET /scheduling/series/{series} \u2014 a series' template + every session it generated","tags":["Scheduling & Bookings"],"parameters":[{"name":"series","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"series":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"session_type":{"type":"string"},"event_concept_id":{"type":"string"},"club":{},"club_id":{"type":"string"},"area_id":{"type":"string"},"trainer_id":{"type":"string"},"capacity":{"type":"string"},"weekdays":{"type":"string"},"time":{"type":"string"},"starts_on":{"type":"string"},"ends_on":{"type":"string"}},"required":["id","concept","session_type","event_concept_id","club","club_id","area_id","trainer_id","capacity","weekdays","time","starts_on","ends_on"]},"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"session_type":{"type":"string"},"area":{"type":["string","null"]},"trainer":{"type":["string","null"]},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"capacity":{"type":"integer"},"booked":{"type":"integer"},"status":{"type":"string"},"access_type":{"type":"string"}},"required":["id","concept","session_type","area","trainer","starts_at","ends_at","capacity","booked","status","access_type"]}}},"required":["series","sessions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"scheduling.updateSeries","summary":"PATCH /scheduling/series/{series} \u2014 edit the series (trainer / room / capacity / time) and propagate\nthe change to its FUTURE, non-cancelled occurrences. Booked members are notified once","tags":["Scheduling & Bookings"],"parameters":[{"name":"series","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"trainer_id":{"type":"string"},"area_id":{"type":"string"},"capacity":{"type":"integer","minimum":1,"maximum":1000},"time":{"type":"string"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"updated":{"type":"integer","minimum":0}},"required":["updated"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/series/{series}/cancel-future":{"post":{"operationId":"scheduling.cancelSeriesFuture","summary":"POST /scheduling/series/{series}/cancel-future \u2014 cancel every upcoming occurrence of the series","tags":["Scheduling & Bookings"],"parameters":[{"name":"series","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"cancelled":{"type":"integer","minimum":0},"notified":{"type":"integer"}},"required":["cancelled","notified"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/{session}":{"get":{"operationId":"scheduling.show","summary":"GET /scheduling/sessions/{session} \u2014 one session's full detail row (drives the detail page)","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"session":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"session_type":{"type":"string"},"club":{},"area":{},"trainer":{"type":["string","null"]},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"capacity":{"type":"string"},"booked":{"type":"integer"},"status":{"type":"string"},"access_type":{"anyOf":[{"type":"string"},{"type":"string","enum":["open"]}]},"series_id":{"type":"string"}},"required":["id","concept","session_type","club","area","trainer","starts_at","ends_at","capacity","booked","status","access_type","series_id"]}},"required":["session"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"scheduling.update","summary":"PATCH /scheduling/sessions/{session} \u2014 edit a session's time / room / capacity (RFP 14 #7). Re-checks\ntrainer+room conflicts, preserves bookings, notifies booked members","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"starts_at":{"type":["string","null"],"format":"date-time"},"area_id":{"type":["string","null"]},"capacity":{"type":["integer","null"],"minimum":1,"maximum":1000},"scope":{"type":["string","null"],"enum":["this","future"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"starts_at":{"type":"string"},"notified":{"type":"integer"}},"required":["id","starts_at","notified"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/options":{"get":{"operationId":"scheduling.options","summary":"Self-contained form options for scheduling a session: clubs, areas, concepts, trainers","tags":["Scheduling & Bookings"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"areas":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"club_id":{"type":"string"}},"required":["id","name","club_id"]}},"concepts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"session_type":{"type":"string"},"duration_minutes":{"type":"integer"},"capacity":{"type":"integer"}},"required":["id","name","session_type","duration_minutes","capacity"]}},"trainers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"club_id":{"type":["string","null"]},"concept_ids":{"description":"home branch \u2014 the form filters trainers to the chosen club","anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]}},"required":["id","name","club_id","concept_ids"]}}},"required":["clubs","areas","concepts","trainers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/{session}/bookings":{"get":{"operationId":"scheduling.bookings","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member":{"type":["string","null"]},"status":{"type":"string","description":"Raw so a legacy out-of-enum status can't 500 the roster."}},"required":["id","member","status"]}}},"required":["bookings"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/recurring":{"post":{"operationId":"scheduling.recurring","summary":"POST /scheduling/sessions/recurring \u2014 generate a recurring series (RFP 14 #3): a weekly pattern over\na date range, skipping holiday dates and any occurrence that hits a conflict (reported, not failed)","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_concept_id":{"type":"string"},"club_id":{"type":"string"},"area_id":{"type":"string"},"trainer_id":{"type":"string"},"time":{"type":"string"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"weekdays":{"type":"array","items":{"type":"integer","minimum":0,"maximum":6},"minItems":1},"skip_dates":{"type":["array","null"],"items":{"type":"string","format":"date-time"}}},"required":["event_concept_id","club_id","area_id","trainer_id","time","from","to","weekdays"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"},"skipped":{"type":"string"}},"required":["created","skipped"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/recurring/bulk":{"post":{"operationId":"scheduling.recurringBulk","summary":"POST /scheduling/sessions/recurring/bulk \u2014 create SEVERAL recurring series in one request (e.g. a\nHIIT course for Trainer A + a Yoga course for Trainer B). Best-effort per series: a conflict on one\nseries never blocks the others; each series returns its own {created, skipped, error} so staff see\nexactly what landed and what didn't","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"series":{"type":"array","items":{"type":"object","properties":{"event_concept_id":{"type":"string"},"club_id":{"type":"string"},"area_id":{"type":"string"},"trainer_id":{"type":"string"},"weekdays":{"type":"array","items":{"type":"integer","minimum":0,"maximum":6},"minItems":1},"time":{"type":"string"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"skip_dates":{"type":["array","null"],"items":{"type":"string","format":"date-time"}}},"required":["event_concept_id","club_id","area_id","trainer_id","weekdays","time","from","to"]},"minItems":1,"maxItems":50}},"required":["series"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"created":{"type":"integer"},"skipped":{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false},"error":{"type":"string","const":"The end date is before the start date."}},"required":["created","skipped","error"]},{"type":"object","properties":{"created":{"type":"integer"},"skipped":{"type":"string"},"error":{"type":"null"}},"required":["created","skipped","error"]},{"type":"object","properties":{"created":{"type":"integer"},"skipped":{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false},"error":{"type":"string"}},"required":["created","skipped","error"]}]}}},"required":["results"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/bulk-cancel":{"post":{"operationId":"scheduling.bulkCancelSessions","summary":"POST /scheduling/sessions/bulk-cancel { session_ids:[] } \u2014 cancel multiple sessions in one action\n(RFP 14 #8), e.g. every class in a room for a maintenance window. Each restores credits + notifies","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"session_ids":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":500}},"required":["session_ids"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"cancelled":{"type":"integer"},"notified":{"type":"integer"}},"required":["cancelled","notified"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/{session}/access-codes":{"get":{"operationId":"scheduling.accessCodes","summary":"GET /scheduling/sessions/{session}/access-codes \u2014 VIP access codes + usage (staff distribute these)","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"codes":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"used":{"type":"boolean"},"max_uses":{"type":"integer"}},"required":["code","used","max_uses"]}}},"required":["codes"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/{session}/cancel":{"post":{"operationId":"scheduling.cancelSession","summary":"POST /scheduling/sessions/{session}/cancel \u2014 cancel the whole session (RFP 14 #8). Cancels every\nbooking, restores any reserved PT credit (no-charge, RFP 14 #19), and notifies members","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"scope":{"type":["string","null"],"enum":["this","future"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"cancelled":{"type":"integer","minimum":0},"notified":{"type":"integer"}},"required":["cancelled","notified"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/{session}/book":{"post":{"operationId":"scheduling.book","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"access_code":{"type":["string","null"]},"booked_by_employee_id":{"type":["string","null"]}},"required":["member_id"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/bookings/{booking}/attend":{"post":{"operationId":"scheduling.attend","summary":"Staff marks attendance: confirm both parties, then conduct (emits commission)","tags":["Scheduling & Bookings"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"type":"string","enum":["show","no_show"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/{session}/conduct":{"post":{"operationId":"scheduling.conductSession","summary":"POST /scheduling/sessions/{session}/conduct \u2014 staff bulk-close a GX roster (vouched exceptions conduct directly, audited)","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"exceptions":{"type":["array","null"],"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"conducted":{"type":"integer"},"no_show":{"type":"integer"},"overrides":{"type":"integer"},"conflicts":{"type":"integer"}},"required":["conducted","no_show","overrides","conflicts"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/held-commissions":{"get":{"operationId":"scheduling.heldCommissions","summary":"GET /scheduling/held-commissions \u2014 conducted on member evidence with the trainer still\nsilent: held until the trainer confirms (releasable inside the policy deadline)","tags":["Scheduling & Bookings"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"held":{"type":"array","items":{"type":"object","properties":{"booking_id":{"type":"string"},"session_id":{"type":"string"},"trainer_id":{"type":"string"},"concept_name":{"type":"string"},"starts_at":{"type":"string"},"commission_minor":{"type":"integer"},"deadline_at":{"type":"string"},"state":{"type":"string","enum":["expired","held"]}},"required":["booking_id","session_id","trainer_id","concept_name","starts_at","commission_minor","deadline_at","state"]}}},"required":["held"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/bookings/{booking}/cancel":{"post":{"operationId":"scheduling.cancelBooking","summary":"POST /scheduling/bookings/{booking}/cancel \u2014 staff cancel on a member's behalf (RFP 14 #18). A\nmandatory reason code is recorded with who cancelled (audit). \"No Charge\" waives the late-cancel\npenalty (refunds the credit, no strike) and is MANAGER-ONLY \u2014 receptionists can only Charge","tags":["Scheduling & Bookings"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason_code":{"type":"string","maxLength":120},"no_charge":{"type":"boolean"}},"required":["reason_code"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"no_charge":{"type":"boolean"}},"required":["status","no_charge"]}}}},"403":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"Only a manager can cancel with No Charge."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/sessions/{session}/substitute":{"patch":{"operationId":"scheduling.substitute","summary":"PATCH /scheduling/sessions/{session}/substitute { trainer_id } \u2014 instructor substitution (RFP 14\n#12). Swaps the trainer (must be eligible + free), keeps every booking, and notifies the booked\nmembers; commission follows the substitute automatically because conduction reads the session's\ntrainer_id","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"trainer_id":{"type":"string"},"scope":{"type":["string","null"],"enum":["this","future"]}},"required":["trainer_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"trainer_id":{"type":"string"},"notified":{"type":"integer"}},"required":["trainer_id","notified"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/pt/trainers":{"get":{"operationId":"scheduling.ptTrainers","summary":"GET /scheduling/pt/trainers?club_id= \u2014 PT-eligible trainers at a branch (staff PT booking)","tags":["Scheduling & Bookings"],"parameters":[{"name":"club_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"trainers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"photo_url":{"type":["string","null"]}},"required":["id","name","name_ar","photo_url"]}}},"required":["trainers"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/pt/slots":{"get":{"operationId":"scheduling.ptSlots","summary":"GET /scheduling/pt/slots?trainer_id=&date= \u2014 open PT start-times (gated by trainer availability)","tags":["Scheduling & Bookings"],"parameters":[{"name":"trainer_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"date","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"slots":{"type":"array","items":{"type":"object","properties":{"starts_at":{"type":"string"},"label":{"type":"string"}},"required":["starts_at","label"]}}},"required":["slots"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/pt/book":{"post":{"operationId":"scheduling.ptBook","summary":"POST /scheduling/pt/book \u2014 front desk books a member into a PT slot (RFP 14 #16); reserves a credit","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"club_id":{"type":"string"},"trainer_id":{"type":"string"},"starts_at":{"type":"string","format":"date-time"},"booked_by_employee_id":{"type":["string","null"]}},"required":["member_id","club_id","trainer_id","starts_at"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"booking_id":{"type":"string"},"status":{"type":"string"}},"required":["booking_id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/scheduling/pt/recurring":{"post":{"operationId":"scheduling.ptRecurring","summary":"POST /scheduling/pt/recurring \u2014 a standing weekly PT series for a member (RFP 23 #39)","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"member_id":{"type":"string"},"club_id":{"type":"string"},"trainer_id":{"type":"string"},"time":{"type":"string"},"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"booked_by_employee_id":{"type":["string","null"]},"weekdays":{"type":"array","items":{"type":"integer","minimum":0,"maximum":6},"minItems":1}},"required":["member_id","club_id","trainer_id","time","from","to","weekdays"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"created":{"type":"integer"},"skipped":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"date":{"type":"string"},"reason":{"type":"string","const":"no_credits"}},"required":["date","reason"]},{"type":"object","properties":{"date":{"type":"string"},"reason":{"type":"string"}},"required":["date","reason"]}]}}},"required":["created","skipped"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/strikes/{strike}/waive":{"post":{"operationId":"strike.waive","summary":"POST /strikes/{strike}/waive { reason } \u2014 manager waives a strike (illness, goodwill)","tags":["Scheduling & Bookings"],"parameters":[{"name":"strike","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":255}},"required":["reason"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"waived":{"type":"boolean"}},"required":["waived"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/booking-bans/{ban}/lift":{"post":{"operationId":"strike.liftBan","summary":"POST /booking-bans/{ban}/lift { reason } \u2014 manager lifts an active booking ban early (audited)","tags":["Scheduling & Bookings"],"parameters":[{"name":"ban","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":255}},"required":["reason"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"lifted":{"type":"boolean"}},"required":["lifted"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/trainer/sessions":{"get":{"operationId":"trainerSession.index","summary":"GET /trainer/sessions \u2014 bookings under the acting trainer's sessions, with member + status","tags":["Scheduling & Bookings"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"booking_id":{"type":"string"},"session_id":{"type":"string"},"trainer_id":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"member":{"type":["string","null"]},"status":{"type":"string","description":"Raw string (not the enum cast) so a legacy/unknown status (e.g. 'attended') can't 500 the list."},"disputed_at":{"type":"string"},"dispute_reason":{"type":["string","null"]}},"required":["booking_id","session_id","trainer_id","starts_at","ends_at","member","status","disputed_at","dispute_reason"]}}},"required":["sessions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/trainer/sessions/{booking}/attendance":{"post":{"operationId":"trainerSession.attendance","summary":"POST /trainer/sessions/{booking}/attendance {outcome: show|no_show} \u2014 the acting trainer records\nattendance for one of their own bookings. `show` conducts the session (emits commission); `no_show`\nmarks the member absent (a reserved PT credit is forfeited)","tags":["Scheduling & Bookings"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"outcome":{"type":"string","enum":["show","no_show"]}},"required":["outcome"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string"}},"required":["id","status"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Not your session."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/trainer/sessions/{session}/conduct":{"post":{"operationId":"trainerSession.conduct","summary":"POST /trainer/sessions/{session}/conduct {exceptions?: [booking ids]} \u2014 GX bulk close:\nchecked-in roster \u2192 conducted (commission), vouched exceptions \u2192 FM override approvals,\neveryone else \u2192 no-show","tags":["Scheduling & Bookings"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"exceptions":{"type":["array","null"],"items":{"type":"string"}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"conducted":{"type":"integer"},"no_show":{"type":"integer"},"overrides":{"type":"integer"},"conflicts":{"type":"integer"}},"required":["conducted","no_show","overrides","conflicts"]}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Not your session."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/trainer/sessions/{booking}/dispute":{"post":{"operationId":"trainerSession.dispute","summary":"POST /trainer/sessions/{booking}/dispute {reason} \u2014 the acting trainer flags one of\ntheir own bookings as disputed. The commission block is a note here; full commission\ngating is Module 08","tags":["Scheduling & Bookings"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":500}},"required":["reason"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"disputed_at":{"type":"string"},"dispute_reason":{"type":"string"},"commission_note":{"type":"string","description":"Note: commission for a disputed conduction is held pending review (Module 08).","const":"Commission for this session is held pending dispute review."}},"required":["id","disputed_at","dispute_reason","commission_note"]}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Not your session."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/trainer-sessions":{"get":{"operationId":"trainerSession.adminIndex","summary":"GET /trainer-sessions?trainer_id= \u2014 admin view of any trainer's sessions (scheduling.manage)","tags":["Scheduling & Bookings"],"parameters":[{"name":"trainer_id","in":"query","schema":{"type":["string","null"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","properties":{"booking_id":{"type":"string"},"session_id":{"type":"string"},"trainer_id":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"member":{"type":["string","null"]},"status":{"type":"string","description":"Raw string (not the enum cast) so a legacy/unknown status (e.g. 'attended') can't 500 the list."},"disputed_at":{"type":"string"},"dispute_reason":{"type":["string","null"]}},"required":["booking_id","session_id","trainer_id","starts_at","ends_at","member","status","disputed_at","dispute_reason"]}}},"required":["sessions"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/trainer-sessions/{booking}/resolve-dispute":{"patch":{"operationId":"trainerSession.resolveDispute","summary":"PATCH /trainer-sessions/{booking}/resolve-dispute \u2014 clear a dispute (scheduling.manage)","tags":["Scheduling & Bookings"],"parameters":[{"name":"booking","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"disputed_at":{"type":"null"}},"required":["id","disputed_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers":{"get":{"operationId":"manageTrainer.index","summary":"GET /manage/trainers \u2014 the trainer roster with at-a-glance stats","tags":["Scheduling & Bookings"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"trainers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"gender":{"type":"string"},"photo_url":{"type":["string","null"]},"club_name":{"type":"string"},"hr_tier":{"type":["string","null"]},"employment_type":{"type":"string"},"commission_tier":{"type":"string"},"is_active":{"type":"boolean"},"eligibility_count":{"type":"integer"},"upcoming_sessions":{"type":"integer"},"has_login":{"type":"boolean"}},"required":["id","name","name_ar","gender","photo_url","club_name","hr_tier","employment_type","commission_tier","is_active","eligibility_count","upcoming_sessions","has_login"]}}},"required":["trainers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"manageTrainer.store","summary":"POST /manage/trainers \u2014 create a trainer (+ optional eligibility + login)","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","maxLength":120},"last_name":{"type":["string","null"],"maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"national_id":{"type":["string","null"],"maxLength":20},"gender":{"$ref":"#/components/schemas/Gender"},"club_id":{"type":"string"},"certifications":{"type":["string","null"],"maxLength":2000},"certifications_ar":{"type":["string","null"],"maxLength":2000},"bio":{"type":["string","null"],"maxLength":2000},"bio_ar":{"type":["string","null"],"maxLength":2000},"hr_tier":{"type":"string","maxLength":60},"commission_tier_id":{"type":["string","null"]},"employment_type":{"type":"string","enum":["employee","freelance"]},"is_active":{"type":"boolean"},"eligibility":{"type":["array","null"],"items":{"type":"string"}}},"required":["first_name","club_id","hr_tier"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers/{employee}":{"get":{"operationId":"manageTrainer.show","summary":"GET /manage/trainers/{employee} \u2014 full profile: eligibility, recent sessions, commission","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"first_name_ar":{"type":"string"},"last_name_ar":{"type":"string"},"national_id":{"type":"string"},"gender":{"type":"string"},"photo_url":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"certifications":{"type":"string"},"certifications_ar":{"type":"string"},"bio":{"type":"string"},"bio_ar":{"type":"string"},"hr_tier":{"type":"string"},"commission_tier_id":{"type":"string"},"commission_tier":{"type":"string"},"employment_type":{"type":"string"},"is_active":{"type":"boolean"},"eligibility":{"type":"object","additionalProperties":{}},"has_login":{"type":"boolean"},"login_email":{"type":"string"},"commission_earned_minor":{"type":"integer"},"rating_avg":{"type":["number","null"]},"rating_count":{"type":"integer","minimum":0},"availability_days":{"type":"integer","minimum":0},"available_hours":{"type":"number","description":"Utilization (RFP refinement): hours the trainer made available vs hours actually booked."},"booked_hours":{"type":"number"},"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"starts_at":{"type":"string"},"status":{"type":"string","description":"Raw string (not the enum cast) so a legacy/unknown status value can't 500 the page."}},"required":["id","concept","starts_at","status"]}}},"required":["id","first_name","last_name","first_name_ar","last_name_ar","national_id","gender","photo_url","club_id","club_name","certifications","certifications_ar","bio","bio_ar","hr_tier","commission_tier_id","commission_tier","employment_type","is_active","eligibility","has_login","login_email","commission_earned_minor","rating_avg","rating_count","availability_days","available_hours","booked_hours","sessions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"manageTrainer.update","summary":"PATCH /manage/trainers/{employee} \u2014 update profile + active flag","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","maxLength":120},"last_name":{"type":["string","null"],"maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"national_id":{"type":["string","null"],"maxLength":20},"gender":{"$ref":"#/components/schemas/Gender"},"club_id":{"type":"string","description":"Branch is required \u2014 a trainer must belong to a club (drives the scheduling filter)."},"certifications":{"type":["string","null"],"maxLength":2000},"certifications_ar":{"type":["string","null"],"maxLength":2000},"bio":{"type":["string","null"],"maxLength":2000},"bio_ar":{"type":["string","null"],"maxLength":2000},"hr_tier":{"type":"string","maxLength":60},"commission_tier_id":{"type":["string","null"],"description":"Commission tier (rate + per-period session cap) + employment type (client RFP refinement)."},"employment_type":{"type":"string","enum":["employee","freelance"]},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"first_name_ar":{"type":"string"},"last_name_ar":{"type":"string"},"national_id":{"type":"string"},"gender":{"type":"string"},"photo_url":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"certifications":{"type":"string"},"certifications_ar":{"type":"string"},"bio":{"type":"string"},"bio_ar":{"type":"string"},"hr_tier":{"type":"string"},"commission_tier_id":{"type":"string"},"commission_tier":{"type":"string"},"employment_type":{"type":"string"},"is_active":{"type":"boolean"},"eligibility":{"type":"object","additionalProperties":{}},"has_login":{"type":"boolean"},"login_email":{"type":"string"},"commission_earned_minor":{"type":"integer"},"rating_avg":{"type":["number","null"]},"rating_count":{"type":"integer","minimum":0},"availability_days":{"type":"integer","minimum":0},"available_hours":{"type":"number","description":"Utilization (RFP refinement): hours the trainer made available vs hours actually booked."},"booked_hours":{"type":"number"},"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"starts_at":{"type":"string"},"status":{"type":"string","description":"Raw string (not the enum cast) so a legacy/unknown status value can't 500 the page."}},"required":["id","concept","starts_at","status"]}}},"required":["id","first_name","last_name","first_name_ar","last_name_ar","national_id","gender","photo_url","club_id","club_name","certifications","certifications_ar","bio","bio_ar","hr_tier","commission_tier_id","commission_tier","employment_type","is_active","eligibility","has_login","login_email","commission_earned_minor","rating_avg","rating_count","availability_days","available_hours","booked_hours","sessions"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers/{employee}/three-sixty":{"get":{"operationId":"manageTrainer.threeSixty","summary":"GET /manage/trainers/{employee}/three-sixty \u2014 the tabular datasets that power the\n360 profile: the trainer's schedule (upcoming + recent sessions with booked counts),\ntheir clients (members who've booked their sessions), the commission ledger, and\nmember ratings. Kept separate from show() so the edit forms stay lean","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"object","properties":{"upcoming":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"club":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"capacity":{"type":"integer"},"booked_count":{"type":"integer"},"status":{"type":"string"}},"required":["id","concept","club","starts_at","ends_at","capacity","booked_count","status"]}},"recent":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"club":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"},"capacity":{"type":"integer"},"booked_count":{"type":"integer"},"status":{"type":"string"}},"required":["id","concept","club","starts_at","ends_at","capacity","booked_count","status"]}}},"required":["upcoming","recent"]},"clients":{"type":"string"},"clients_count":{"type":"integer"},"commissions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"posted_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","source","amount_minor","currency","status","posted_at","created_at"]}},"ratings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member":{"type":["string","null"]},"member_ar":{"type":["string","null"]},"stars":{"type":"integer"},"comment":{"type":["string","null"]},"created_at":{"type":"string"}},"required":["id","member","member_ar","stars","comment","created_at"]}}},"required":["sessions","clients","clients_count","commissions","ratings"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers/{employee}/eligibility":{"put":{"operationId":"manageTrainer.setEligibility","summary":"PUT /manage/trainers/{employee}/eligibility \u2014 set the class types this trainer may teach","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"eligibility":{"type":"array","items":{"type":"string"}}},"required":["eligibility"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"first_name_ar":{"type":"string"},"last_name_ar":{"type":"string"},"national_id":{"type":"string"},"gender":{"type":"string"},"photo_url":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"certifications":{"type":"string"},"certifications_ar":{"type":"string"},"bio":{"type":"string"},"bio_ar":{"type":"string"},"hr_tier":{"type":"string"},"commission_tier_id":{"type":"string"},"commission_tier":{"type":"string"},"employment_type":{"type":"string"},"is_active":{"type":"boolean"},"eligibility":{"type":"object","additionalProperties":{}},"has_login":{"type":"boolean"},"login_email":{"type":"string"},"commission_earned_minor":{"type":"integer"},"rating_avg":{"type":["number","null"]},"rating_count":{"type":"integer","minimum":0},"availability_days":{"type":"integer","minimum":0},"available_hours":{"type":"number","description":"Utilization (RFP refinement): hours the trainer made available vs hours actually booked."},"booked_hours":{"type":"number"},"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"starts_at":{"type":"string"},"status":{"type":"string","description":"Raw string (not the enum cast) so a legacy/unknown status value can't 500 the page."}},"required":["id","concept","starts_at","status"]}}},"required":["id","first_name","last_name","first_name_ar","last_name_ar","national_id","gender","photo_url","club_id","club_name","certifications","certifications_ar","bio","bio_ar","hr_tier","commission_tier_id","commission_tier","employment_type","is_active","eligibility","has_login","login_email","commission_earned_minor","rating_avg","rating_count","availability_days","available_hours","booked_hours","sessions"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers/{employee}/availability":{"get":{"operationId":"manageTrainer.availability","summary":"GET /manage/trainers/{employee}/availability \u2014 the trainer's submitted windows for the next 14 days","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"windows":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]}},"required":["date","windows"]}}},"required":["days"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"manageTrainer.setAvailability","summary":"PUT /manage/trainers/{employee}/availability \u2014 staff set a date's availability windows. Managers are\nnot bound by the trainer-app change deadline (RFP 25 #4): they can edit any upcoming date","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"windows":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]}}},"required":["date","windows"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"windows":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]}},"required":["date","windows"]}}},"required":["days"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers/{employee}/availability/copy":{"post":{"operationId":"manageTrainer.copyAvailability","summary":"POST /manage/trainers/{employee}/availability/copy \u2014 copy one day's windows onto other dates (RFP refinement)","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"from_date":{"type":"string","format":"date-time"},"to_dates":{"type":"array","items":{"type":"string","format":"date-time"},"minItems":1}},"required":["from_date","to_dates"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"windows":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]}},"required":["date","windows"]}}},"required":["days"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers/{employee}/availability/template":{"get":{"operationId":"manageTrainer.availabilityTemplate","summary":"GET /manage/trainers/{employee}/availability/template \u2014 the weekly recurring pattern (RFP UX)","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{}},"required":["pattern"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"manageTrainer.setAvailabilityTemplate","summary":"PUT /manage/trainers/{employee}/availability/template \u2014 save the weekly pattern + fill upcoming dates","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]}}}},"required":["pattern"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"string"}},"required":["pattern"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers/{employee}/login":{"post":{"operationId":"manageTrainer.setLogin","summary":"POST /manage/trainers/{employee}/login \u2014 create or reset the trainer's app login","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","maxLength":190},"password":{"type":"string","minLength":8}},"required":["email","password"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"first_name_ar":{"type":"string"},"last_name_ar":{"type":"string"},"national_id":{"type":"string"},"gender":{"type":"string"},"photo_url":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"certifications":{"type":"string"},"certifications_ar":{"type":"string"},"bio":{"type":"string"},"bio_ar":{"type":"string"},"hr_tier":{"type":"string"},"commission_tier_id":{"type":"string"},"commission_tier":{"type":"string"},"employment_type":{"type":"string"},"is_active":{"type":"boolean"},"eligibility":{"type":"object","additionalProperties":{}},"has_login":{"type":"boolean"},"login_email":{"type":"string"},"commission_earned_minor":{"type":"integer"},"rating_avg":{"type":["number","null"]},"rating_count":{"type":"integer","minimum":0},"availability_days":{"type":"integer","minimum":0},"available_hours":{"type":"number","description":"Utilization (RFP refinement): hours the trainer made available vs hours actually booked."},"booked_hours":{"type":"number"},"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"starts_at":{"type":"string"},"status":{"type":"string","description":"Raw string (not the enum cast) so a legacy/unknown status value can't 500 the page."}},"required":["id","concept","starts_at","status"]}}},"required":["id","first_name","last_name","first_name_ar","last_name_ar","national_id","gender","photo_url","club_id","club_name","certifications","certifications_ar","bio","bio_ar","hr_tier","commission_tier_id","commission_tier","employment_type","is_active","eligibility","has_login","login_email","commission_earned_minor","rating_avg","rating_count","availability_days","available_hours","booked_hours","sessions"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainers/{employee}/photo":{"post":{"operationId":"manageTrainer.uploadPhoto","summary":"POST /manage/trainers/{employee}/photo \u2014 upload the trainer's profile photo","tags":["Scheduling & Bookings"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"photo":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","maxLength":4096}},"required":["photo"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"first_name_ar":{"type":"string"},"last_name_ar":{"type":"string"},"national_id":{"type":"string"},"gender":{"type":"string"},"photo_url":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"certifications":{"type":"string"},"certifications_ar":{"type":"string"},"bio":{"type":"string"},"bio_ar":{"type":"string"},"hr_tier":{"type":"string"},"commission_tier_id":{"type":"string"},"commission_tier":{"type":"string"},"employment_type":{"type":"string"},"is_active":{"type":"boolean"},"eligibility":{"type":"object","additionalProperties":{}},"has_login":{"type":"boolean"},"login_email":{"type":"string"},"commission_earned_minor":{"type":"integer"},"rating_avg":{"type":["number","null"]},"rating_count":{"type":"integer","minimum":0},"availability_days":{"type":"integer","minimum":0},"available_hours":{"type":"number","description":"Utilization (RFP refinement): hours the trainer made available vs hours actually booked."},"booked_hours":{"type":"number"},"sessions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"concept":{"type":"string"},"starts_at":{"type":"string"},"status":{"type":"string","description":"Raw string (not the enum cast) so a legacy/unknown status value can't 500 the page."}},"required":["id","concept","starts_at","status"]}}},"required":["id","first_name","last_name","first_name_ar","last_name_ar","national_id","gender","photo_url","club_id","club_name","certifications","certifications_ar","bio","bio_ar","hr_tier","commission_tier_id","commission_tier","employment_type","is_active","eligibility","has_login","login_email","commission_earned_minor","rating_avg","rating_count","availability_days","available_hours","booked_hours","sessions"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/booking-policy":{"get":{"operationId":"bookingPolicy.show","summary":"GET /booking-policy \u2014 the current policy (defaults if never saved)","tags":["Scheduling & Bookings"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"policy":{"type":"object","properties":{"enabled":{"type":"boolean"},"strike_threshold":{"type":"integer"},"strike_window_days":{"type":"integer"},"penalty_ban":{"type":"boolean"},"penalty_fee":{"type":"boolean"},"fee_minor":{"type":"integer"},"currency":{"type":"string"},"ban_ladder_days":{"anyOf":[{"type":"array","items":{}},{"type":"array","prefixItems":[{"type":"integer","const":7},{"type":"integer","const":14},{"type":"integer","const":28}],"minItems":3,"maxItems":3,"additionalItems":false}]},"ladder_window_days":{"type":"integer"},"exempt_waitlist":{"type":"boolean"},"exempt_illness":{"type":"boolean"},"exempt_admin_waiver":{"type":"boolean"},"promoted_grace_minutes":{"type":"integer"},"gx_cancel_window_minutes":{"type":"integer"},"pt_cancel_window_minutes":{"type":"integer"},"booking_opens_days_before":{"type":"integer"},"booking_closes_minutes_before":{"type":"integer"},"max_bookings_per_day":{"type":"integer"},"max_bookings_per_week":{"type":"integer"},"offer_window_minutes":{"type":"integer"},"waitlist_no_show_policy":{"type":"string"},"member_confirm_source":{"type":"string","description":"Two-key attendance confirmation."},"pt_member_confirm_source":{"type":["string","null"]},"app_confirm_window_minutes":{"type":"integer"},"require_dual_for_commission":{"type":"boolean"},"trainer_confirm_deadline_hours":{"type":"integer"}},"required":["enabled","strike_threshold","strike_window_days","penalty_ban","penalty_fee","fee_minor","currency","ban_ladder_days","ladder_window_days","exempt_waitlist","exempt_illness","exempt_admin_waiver","promoted_grace_minutes","gx_cancel_window_minutes","pt_cancel_window_minutes","booking_opens_days_before","booking_closes_minutes_before","max_bookings_per_day","max_bookings_per_week","offer_window_minutes","waitlist_no_show_policy","member_confirm_source","pt_member_confirm_source","app_confirm_window_minutes","require_dual_for_commission","trainer_confirm_deadline_hours"]}},"required":["policy"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"bookingPolicy.update","summary":"PUT /booking-policy \u2014 upsert the brand's policy","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"strike_threshold":{"type":"integer","minimum":1,"maximum":20},"strike_window_days":{"type":"integer","minimum":1,"maximum":365},"penalty_ban":{"type":"boolean"},"penalty_fee":{"type":"boolean"},"fee_minor":{"type":"integer","minimum":0},"ladder_window_days":{"type":"integer","minimum":1,"maximum":730},"exempt_waitlist":{"type":"boolean"},"exempt_illness":{"type":"boolean"},"exempt_admin_waiver":{"type":"boolean"},"promoted_grace_minutes":{"type":"integer","minimum":0,"maximum":1440},"gx_cancel_window_minutes":{"type":"integer","minimum":0,"maximum":1440},"pt_cancel_window_minutes":{"type":"integer","minimum":0,"maximum":1440},"booking_opens_days_before":{"type":"integer","description":"Configurable booking window (client RFP refinement) \u2014 optional so older clients keep working.","minimum":1,"maximum":365},"booking_closes_minutes_before":{"type":"integer","minimum":0,"maximum":10080},"max_bookings_per_day":{"type":"integer","description":"Member-wide booking limits (0 = unlimited): most classes a member may hold per day / per week.","minimum":0,"maximum":50},"max_bookings_per_week":{"type":"integer","minimum":0,"maximum":200},"offer_window_minutes":{"type":"integer","description":"Waitlist offer-and-confirm window + how a waitlist no-show is penalised (client RFP refinement).","minimum":5,"maximum":1440},"waitlist_no_show_policy":{"type":"string","enum":["exempt","grace","same_as_direct"]},"member_confirm_source":{"type":"string","description":"Two-key attendance confirmation (member-key evidence source + held-commission window).","enum":["gate","app","either"]},"pt_member_confirm_source":{"type":["string","null"],"enum":["gate","app","either"]},"app_confirm_window_minutes":{"type":"integer","minimum":5,"maximum":240},"require_dual_for_commission":{"type":"boolean"},"trainer_confirm_deadline_hours":{"type":"integer","minimum":1,"maximum":336},"ban_ladder_days":{"type":"array","items":{"type":"integer","minimum":1,"maximum":365},"minItems":1}},"required":["enabled","strike_threshold","strike_window_days","penalty_ban","penalty_fee","fee_minor","ladder_window_days","exempt_waitlist","exempt_illness","exempt_admin_waiver","promoted_grace_minutes","gx_cancel_window_minutes","pt_cancel_window_minutes","ban_ladder_days"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"policy":{"type":"object","properties":{"enabled":{"type":"boolean"},"strike_threshold":{"type":"integer"},"strike_window_days":{"type":"integer"},"penalty_ban":{"type":"boolean"},"penalty_fee":{"type":"boolean"},"fee_minor":{"type":"integer"},"currency":{"type":"string"},"ban_ladder_days":{"anyOf":[{"type":"array","items":{}},{"type":"array","prefixItems":[{"type":"integer","const":7},{"type":"integer","const":14},{"type":"integer","const":28}],"minItems":3,"maxItems":3,"additionalItems":false}]},"ladder_window_days":{"type":"integer"},"exempt_waitlist":{"type":"boolean"},"exempt_illness":{"type":"boolean"},"exempt_admin_waiver":{"type":"boolean"},"promoted_grace_minutes":{"type":"integer"},"gx_cancel_window_minutes":{"type":"integer"},"pt_cancel_window_minutes":{"type":"integer"},"booking_opens_days_before":{"type":"integer"},"booking_closes_minutes_before":{"type":"integer"},"max_bookings_per_day":{"type":"integer"},"max_bookings_per_week":{"type":"integer"},"offer_window_minutes":{"type":"integer"},"waitlist_no_show_policy":{"type":"string"},"member_confirm_source":{"type":"string","description":"Two-key attendance confirmation."},"pt_member_confirm_source":{"type":["string","null"]},"app_confirm_window_minutes":{"type":"integer"},"require_dual_for_commission":{"type":"boolean"},"trainer_confirm_deadline_hours":{"type":"integer"}},"required":["enabled","strike_threshold","strike_window_days","penalty_ban","penalty_fee","fee_minor","currency","ban_ladder_days","ladder_window_days","exempt_waitlist","exempt_illness","exempt_admin_waiver","promoted_grace_minutes","gx_cancel_window_minutes","pt_cancel_window_minutes","booking_opens_days_before","booking_closes_minutes_before","max_bookings_per_day","max_bookings_per_week","offer_window_minutes","waitlist_no_show_policy","member_confirm_source","pt_member_confirm_source","app_confirm_window_minutes","require_dual_for_commission","trainer_confirm_deadline_hours"]}},"required":["policy"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/booking-caps":{"get":{"operationId":"bookingCap.index","summary":"List the brand's booking caps (config screen)","tags":["Scheduling & Bookings"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"caps":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event_concept_id":{"type":["string","null"]},"event_concept":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"period":{"type":"string"},"max_bookings":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","event_concept_id","event_concept","period","max_bookings","is_active"]}}},"required":["caps"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"bookingCap.store","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_concept_id":{"type":["string","null"]},"period":{"$ref":"#/components/schemas/CapPeriod"},"max_bookings":{"type":"integer","minimum":1},"is_active":{"type":"boolean"}},"required":["period","max_bookings"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"event_concept_id":{"type":["string","null"]},"event_concept":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"period":{"type":"string"},"max_bookings":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","event_concept_id","event_concept","period","max_bookings","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/booking-caps/{cap}":{"patch":{"operationId":"bookingCap.update","tags":["Scheduling & Bookings"],"parameters":[{"name":"cap","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"event_concept_id":{"type":["string","null"]},"period":{"$ref":"#/components/schemas/CapPeriod"},"max_bookings":{"type":"integer","minimum":1},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"event_concept_id":{"type":["string","null"]},"event_concept":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]},"period":{"type":"string"},"max_bookings":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","event_concept_id","event_concept","period","max_bookings","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/class-types":{"get":{"operationId":"classType.index","summary":"GET /class-types \u2014 the full catalogue (active + inactive), with how many sessions each has","tags":["Scheduling & Bookings"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"class_types":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"category":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"session_type":{"type":"string"},"gender_restriction":{"type":"string"},"capacity":{"type":"integer"},"duration_minutes":{"type":"integer"},"intensity":{"type":["string","null"]},"commission_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"ratings_enabled":{"type":"boolean"}},"required":["id","code","name","name_ar","category","category_ar","image_url","session_type","gender_restriction","capacity","duration_minutes","intensity","commission_minor","currency","is_active","ratings_enabled"]}}},"required":["class_types"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"classType.store","summary":"POST /class-types \u2014 add a class type","tags":["Scheduling & Bookings"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"category":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"session_type":{"type":"string"},"gender_restriction":{"type":"string"},"capacity":{"type":"integer"},"duration_minutes":{"type":"integer"},"intensity":{"type":["string","null"]},"commission_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"ratings_enabled":{"type":"boolean"}},"required":["id","code","name","name_ar","category","category_ar","image_url","session_type","gender_restriction","capacity","duration_minutes","intensity","commission_minor","currency","is_active","ratings_enabled"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/class-types/upload":{"post":{"operationId":"classType.upload","summary":"POST /class-types/upload \u2014 store a class-type image (S3 when configured, else the local disk)","tags":["Scheduling & Bookings"],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","contentMediaType":"application/octet-stream","maxLength":25600}},"required":["file"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/class-types/{concept}":{"patch":{"operationId":"classType.update","summary":"PATCH /class-types/{concept} \u2014 edit a class type","tags":["Scheduling & Bookings"],"parameters":[{"name":"concept","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"category":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"session_type":{"type":"string"},"gender_restriction":{"type":"string"},"capacity":{"type":"integer"},"duration_minutes":{"type":"integer"},"intensity":{"type":["string","null"]},"commission_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"ratings_enabled":{"type":"boolean"}},"required":["id","code","name","name_ar","category","category_ar","image_url","session_type","gender_restriction","capacity","duration_minutes","intensity","commission_minor","currency","is_active","ratings_enabled"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/class-types/{concept}/toggle":{"post":{"operationId":"classType.toggle","summary":"POST /class-types/{concept}/toggle \u2014 activate / deactivate (hidden from new bookings)","tags":["Scheduling & Bookings"],"parameters":[{"name":"concept","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"category":{"type":["string","null"]},"category_ar":{"type":["string","null"]},"image_url":{"type":["string","null"]},"session_type":{"type":"string"},"gender_restriction":{"type":"string"},"capacity":{"type":"integer"},"duration_minutes":{"type":"integer"},"intensity":{"type":["string","null"]},"commission_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"ratings_enabled":{"type":"boolean"}},"required":["id","code","name","name_ar","category","category_ar","image_url","session_type","gender_restriction","capacity","duration_minutes","intensity","commission_minor","currency","is_active","ratings_enabled"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/onboarding/checklist":{"get":{"operationId":"onboarding.checklist","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"steps":{"type":"array","prefixItems":[{"type":"object","properties":{"key":{"type":"string","const":"branches"},"title":{"type":"string","const":"Add your branch"},"title_ar":{"type":"string","const":"\u0623\u0636\u0641 \u0641\u0631\u0639\u0643"},"description":{"type":"string","const":"Create at least one club/branch so members and zones have a home."},"description_ar":{"type":"string","const":"\u0623\u0646\u0634\u0626 \u0641\u0631\u0639\u064b\u0627 \u0648\u0627\u062d\u062f\u064b\u0627 \u0639\u0644\u0649 \u0627\u0644\u0623\u0642\u0644."},"done":{"type":"boolean"},"count":{"type":"integer","minimum":0},"route":{"type":"string","const":"/facilities"},"permission":{"type":"string","const":"config.manage"}},"required":["key","title","title_ar","description","description_ar","done","count","route","permission"]},{"type":"object","properties":{"key":{"type":"string","const":"plans"},"title":{"type":"string","const":"Create membership plans"},"title_ar":{"type":"string","const":"\u0623\u0646\u0634\u0626 \u0628\u0627\u0642\u0627\u062a \u0627\u0644\u0627\u0634\u062a\u0631\u0627\u0643"},"description":{"type":"string","const":"Define the plans members can buy."},"description_ar":{"type":"string","const":"\u062d\u062f\u0651\u062f \u0627\u0644\u0628\u0627\u0642\u0627\u062a \u0627\u0644\u062a\u064a \u064a\u0634\u062a\u0631\u064a\u0647\u0627 \u0627\u0644\u0623\u0639\u0636\u0627\u0621."},"done":{"type":"boolean"},"count":{"type":"integer","minimum":0},"route":{"type":"string","const":"/catalogue"},"permission":{"type":"string","const":"config.manage"}},"required":["key","title","title_ar","description","description_ar","done","count","route","permission"]},{"type":"object","properties":{"key":{"type":"string","const":"class_types"},"title":{"type":"string","const":"Set up class types"},"title_ar":{"type":"string","const":"\u0623\u0646\u0634\u0626 \u0623\u0646\u0648\u0627\u0639 \u0627\u0644\u062d\u0635\u0635"},"description":{"type":"string","const":"Define the classes & PT session types you offer."},"description_ar":{"type":"string","const":"\u062d\u062f\u0651\u062f \u0623\u0646\u0648\u0627\u0639 \u0627\u0644\u062d\u0635\u0635 \u0648\u062c\u0644\u0633\u0627\u062a \u0627\u0644\u062a\u062f\u0631\u064a\u0628."},"done":{"type":"boolean"},"count":{"type":"integer","minimum":0},"route":{"type":"string","const":"/class-types"},"permission":{"type":"string","const":"config.manage"}},"required":["key","title","title_ar","description","description_ar","done","count","route","permission"]},{"type":"object","properties":{"key":{"type":"string","const":"trainers"},"title":{"type":"string","const":"Add trainers"},"title_ar":{"type":"string","const":"\u0623\u0636\u0641 \u0627\u0644\u0645\u062f\u0631\u0628\u064a\u0646"},"description":{"type":"string","const":"Add your trainers so they can be booked."},"description_ar":{"type":"string","const":"\u0623\u0636\u0641 \u0627\u0644\u0645\u062f\u0631\u0628\u064a\u0646 \u0644\u064a\u062a\u0645\u0643\u0646 \u0627\u0644\u0623\u0639\u0636\u0627\u0621 \u0645\u0646 \u062d\u062c\u0632\u0647\u0645."},"done":{"type":"boolean"},"count":{"type":"integer","minimum":0},"route":{"type":"string","const":"/trainers"},"permission":{"type":"string","const":"config.manage"}},"required":["key","title","title_ar","description","description_ar","done","count","route","permission"]},{"type":"object","properties":{"key":{"type":"string","const":"access"},"title":{"type":"string","const":"Configure access"},"title_ar":{"type":"string","const":"\u0625\u0639\u062f\u0627\u062f \u0635\u0644\u0627\u062d\u064a\u0629 \u0627\u0644\u062f\u062e\u0648\u0644"},"description":{"type":"string","const":"Create an access template that governs gate entry."},"description_ar":{"type":"string","const":"\u0623\u0646\u0634\u0626 \u0642\u0627\u0644\u0628 \u0635\u0644\u0627\u062d\u064a\u0629 \u064a\u062d\u0643\u0645 \u0627\u0644\u062f\u062e\u0648\u0644 \u0645\u0646 \u0627\u0644\u0628\u0648\u0627\u0628\u0627\u062a."},"done":{"type":"boolean"},"count":{"type":"integer","minimum":0},"route":{"type":"string","const":"/membership-config"},"permission":{"type":"string","const":"config.manage"}},"required":["key","title","title_ar","description","description_ar","done","count","route","permission"]},{"type":"object","properties":{"key":{"type":"string","const":"payments"},"title":{"type":"string","const":"Connect your payment gateway"},"title_ar":{"type":"string","const":"\u0627\u0631\u0628\u0637 \u0628\u0648\u0627\u0628\u0629 \u0627\u0644\u062f\u0641\u0639"},"description":{"type":"string","const":"Add your Tap keys so members can pay online."},"description_ar":{"type":"string","const":"\u0623\u0636\u0641 \u0645\u0641\u0627\u062a\u064a\u062d Tap \u0644\u064a\u062a\u0645\u0643\u0646 \u0627\u0644\u0623\u0639\u0636\u0627\u0621 \u0645\u0646 \u0627\u0644\u062f\u0641\u0639."},"done":{"type":"boolean"},"count":{"type":"integer","minimum":0},"route":{"type":"string","const":"/payment-gateways"},"permission":{"type":"string","const":"config.manage"}},"required":["key","title","title_ar","description","description_ar","done","count","route","permission"]},{"type":"object","properties":{"key":{"type":"string","const":"team"},"title":{"type":"string","const":"Invite your team"},"title_ar":{"type":"string","const":"\u0627\u062f\u0639\u064f \u0641\u0631\u064a\u0642\u0643"},"description":{"type":"string","const":"Invite staff and assign their roles."},"description_ar":{"type":"string","const":"\u0627\u062f\u0639\u064f \u0627\u0644\u0645\u0648\u0638\u0641\u064a\u0646 \u0648\u0639\u064a\u0651\u0646 \u0623\u062f\u0648\u0627\u0631\u0647\u0645."},"done":{"type":"boolean"},"count":{"type":["object","null"]},"route":{"type":"string","const":"/users"},"permission":{"type":"string","const":"users.manage"}},"required":["key","title","title_ar","description","description_ar","done","count","route","permission"]},{"type":"object","properties":{"key":{"type":"string","const":"members"},"title":{"type":"string","const":"Add your first members"},"title_ar":{"type":"string","const":"\u0623\u0636\u0641 \u0623\u0648\u0644 \u0623\u0639\u0636\u0627\u0626\u0643"},"description":{"type":"string","const":"Register or import members to go live."},"description_ar":{"type":"string","const":"\u0633\u062c\u0651\u0644 \u0623\u0648 \u0627\u0633\u062a\u0648\u0631\u062f \u0627\u0644\u0623\u0639\u0636\u0627\u0621 \u0644\u0644\u0627\u0646\u0637\u0644\u0627\u0642."},"done":{"type":"boolean"},"count":{"type":"integer","minimum":0},"route":{"type":"string","const":"/members"},"permission":{"type":"string","const":"members.manage"}},"required":["key","title","title_ar","description","description_ar","done","count","route","permission"]}],"minItems":8,"maxItems":8,"additionalItems":false},"completed":{"type":"integer"},"total":{"type":"integer"},"progress":{"type":"integer"}},"required":["steps","completed","total","progress"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/legal":{"get":{"operationId":"legal.index","summary":"Staff (config.manage): both documents for the brand, with blank placeholders for unset types","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"documents":{"type":"array","items":{}}},"required":["documents"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/legal/{type}":{"put":{"operationId":"legal.update","summary":"Staff (config.manage): upsert one document's bilingual title + body","tags":["Settings & Configuration"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":["string","null"],"maxLength":200},"title_ar":{"type":["string","null"],"maxLength":200},"body":{"type":["string","null"],"maxLength":60000},"body_ar":{"type":["string","null"],"maxLength":60000}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":"string"},"body":{"type":"string"},"body_ar":{"type":"string"},"updated_at":{"type":"string"}},"required":["type","title","title_ar","body","body_ar","updated_at"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/membership-types":{"get":{"operationId":"plan.indexTypes","summary":"#1/#3 \u2014 list the brand's membership types/groups (the plan taxonomy)","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"membership_types":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","description","is_active"]}}},"required":["membership_types"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"plan.storeType","summary":"#1/#3 \u2014 create a membership type/group","tags":["Settings & Configuration"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"description":{"type":["string","null"],"maxLength":1000},"is_active":{"type":["boolean","null"]}},"required":["name"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","description","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/membership-types/{type}":{"patch":{"operationId":"plan.updateType","summary":"#1/#3 \u2014 update a membership type/group","tags":["Settings & Configuration"],"parameters":[{"name":"type","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"name_ar":{"type":["string","null"],"maxLength":120},"description":{"type":["string","null"],"maxLength":1000},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"description":{"type":["string","null"]},"is_active":{"type":"boolean"}},"required":["id","name","name_ar","description","is_active"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/branding":{"get":{"operationId":"branding.selfShow","summary":"GET /branding \u2014 the acting brand views its OWN branding (staff self-service, config.manage)","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"primary_color":{"type":"string"},"logo":{"type":"string"},"portal_name":{"type":"string"},"portal_name_ar":{"type":"string"}},"required":["primary_color","logo","portal_name","portal_name_ar"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"branding.selfUpdate","summary":"PUT /branding \u2014 the acting brand sets its OWN branding (staff self-service, config.manage)","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"primary_color":{"type":"string"},"logo":{"type":"string"},"portal_name":{"type":"string"},"portal_name_ar":{"type":"string"}},"required":["primary_color","logo","portal_name","portal_name_ar"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/commission-target-tiers":{"put":{"operationId":"commissionTarget.saveTiers","summary":"PUT /manage/commission-target-tiers \u2014 replace the brand's accelerator bands","tags":["Settings & Configuration"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tiers":{"type":"array","items":{"type":"object","properties":{"from_pct":{"type":"integer","minimum":0,"maximum":1000},"rate_multiplier":{"type":"number","minimum":0,"maximum":10}},"required":["from_pct","rate_multiplier"]}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"month":{"type":"string"},"tiers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"from_pct":{"type":"integer"},"rate_multiplier":{"type":"number"}},"required":["id","from_pct","rate_multiplier"]}},"targets":{"type":"array","items":{"type":"object","properties":{"employee_id":{"type":"string"},"name":{"type":"string"},"target_amount_minor":{"type":"string"},"target_count":{"type":"string"},"achievement_pct":{"type":["number","null"]},"multiplier":{"type":"number"}},"required":["employee_id","name","target_amount_minor","target_count","achievement_pct","multiplier"]}}},"required":["month","tiers","targets"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/currencies":{"get":{"operationId":"fee.currencies","summary":"#48: the static supported-currency set with the brand default flagged","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"currencies":{"type":"array","items":{}}},"required":["currencies"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-levels":{"get":{"operationId":"classWorkout.levels","summary":"GET /manage/workout-levels \u2014 the brand's level ladder","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"levels":{"type":"array","items":{"$ref":"#/components/schemas/WorkoutLevel"}}},"required":["levels"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"classWorkout.saveLevels","summary":"PUT /manage/workout-levels \u2014 replace the brand's level ladder (name/order/default)","tags":["Settings & Configuration"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"levels":{"type":"array","items":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":"string","maxLength":40},"name_ar":{"type":["string","null"],"maxLength":40},"is_default":{"type":["boolean","null"]}},"required":["name"]},"minItems":1}},"required":["levels"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"levels":{"type":"array","items":{"$ref":"#/components/schemas/WorkoutLevel"}}},"required":["levels"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/trainer-calendar":{"get":{"operationId":"manageTrainer.calendar","summary":"GET /manage/trainer-calendar?from=&to= \u2014 every trainer's availability windows + scheduled sessions\nin one combined calendar (RFP refinement)","tags":["Settings & Configuration"],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"trainers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"availability":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"windows":{"type":"array","items":{}}},"required":["date","windows"]}}]},"sessions":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"type":{"type":"string"},"starts_at":{"type":"string"},"ends_at":{"type":"string"}},"required":["id","title","type","starts_at","ends_at"]}}]}},"required":["id","name","availability","sessions"]}}},"required":["trainers"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/nps/overview":{"get":{"operationId":"nps.overview","summary":"GET /manage/nps/overview \u2014 the NPS dashboard payload","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"nps":{"type":"integer"},"responses":{"type":"integer","minimum":0},"promoters":{"type":"integer","minimum":0},"passives":{"type":"string"},"detractors":{"type":"integer","minimum":0},"trend":{"type":"array","items":{}},"recent":{"type":"array","items":{"type":"object","properties":{"member_id":{"type":"string"},"name":{"type":"string"},"score":{"type":"integer"},"category":{"type":"string","enum":["promoter","passive","detractor"]},"comment":{"type":["string","null"]},"responded_at":{"type":"string"}},"required":["member_id","name","score","category","comment","responded_at"]}}},"required":["nps","responses","promoters","passives","detractors","trend","recent"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/pulses":{"get":{"operationId":"pulse.index","summary":"GET /manage/pulses \u2014 the whole library (seeded on demand)","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"pulses":{"type":"array","items":{"type":"object","properties":{"event":{"type":"string"},"trigger":{"type":"string"},"scale":{"type":"string"},"is_active":{"type":"boolean"},"question_en":{"type":"string"},"question_ar":{"type":"string"},"cooldown_days":{"type":["integer","null"]}},"required":["event","trigger","scale","is_active","question_en","question_ar","cooldown_days"]}}},"required":["pulses"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/pulses/{event}":{"put":{"operationId":"pulse.update","summary":"PUT /manage/pulses/{event} \u2014 reword / toggle / re-scale / re-time one pulse","tags":["Settings & Configuration"],"parameters":[{"name":"event","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"is_active":{"type":"boolean"},"question_en":{"type":"string","maxLength":200},"question_ar":{"type":"string","maxLength":200},"scale":{"type":"string","enum":["nps","csat","thumbs","comment"]},"cooldown_days":{"type":["integer","null"],"minimum":1,"maximum":365}},"required":["is_active","question_en","question_ar","scale"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"pulse":{"type":"object","properties":{"event":{"type":"string"},"trigger":{"type":"string"},"scale":{"type":"string"},"is_active":{"type":"boolean"},"question_en":{"type":"string"},"question_ar":{"type":"string"},"cooldown_days":{"type":["integer","null"]}},"required":["event","trigger","scale","is_active","question_en","question_ar","cooldown_days"]}},"required":["pulse"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/pulses/{event}/results":{"get":{"operationId":"pulse.results","summary":"GET /manage/pulses/{event}/results \u2014 the \"did it work?\" roll-up for one event pulse (reports.view)","tags":["Settings & Configuration"],"parameters":[{"name":"event","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"event":{"type":"string"},"scale":{},"responses":{"type":"integer","minimum":0},"avg":{"type":"number"},"satisfied_pct":{"type":"integer"},"trend":{"type":"array","items":{}},"by_subject":{"type":"string"},"recent":{"type":"array","items":{"type":"object","properties":{"member_id":{"type":"string"},"name":{"type":"string"},"score":{"type":["integer","null"]},"comment":{"type":["string","null"]},"subject_label":{"type":["string","null"]},"responded_at":{"type":"string"}},"required":["member_id","name","score","comment","subject_label","responded_at"]}}},"required":["event","scale","responses","avg","satisfied_pct","trend","by_subject","recent"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/imports/members":{"post":{"operationId":"import.importMembers","summary":"Import a batch of member rows; returns the run summary (imported/skipped/total)","tags":["Settings & Configuration"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","properties":{"national_id":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":["string","null"]},"gender":{"type":"string","enum":["male","female"]},"mobile":{"type":["string","null"]}},"required":["national_id","first_name","gender"]},"minItems":1}},"required":["rows"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"total":{"type":"integer"},"imported":{"type":"integer"},"skipped":{"type":"integer"},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","kind","total","imported","skipped","status","created_at"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/imports":{"get":{"operationId":"import.index","summary":"List this brand's import batches, newest first","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"batches":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"total":{"type":"integer"},"imported":{"type":"integer"},"skipped":{"type":"integer"},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","kind","total","imported","skipped","status","created_at"]}}},"required":["batches"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/app/onboarding":{"get":{"operationId":"mobileApp.onboarding","summary":"GET /manage/app/onboarding \u2014 the brand's onboarding slides, in display order","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"slides":{"type":"array","items":{"type":"string"}}},"required":["slides"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"mobileApp.storeSlide","summary":"POST /manage/app/onboarding \u2014 add a slide","tags":["Settings & Configuration"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"body":{"type":["string","null"]},"body_ar":{"type":["string","null"]},"image":{"type":["string","null"]},"image_url":{"type":["string","null"]},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","title","title_ar","body","body_ar","image","image_url","sort_order","is_active"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/app/onboarding/reorder":{"post":{"operationId":"mobileApp.reorderSlides","summary":"POST /manage/app/onboarding/reorder { ids: [] } \u2014 persist a new slide order","tags":["Settings & Configuration"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ids":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["ids"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"slides":{"type":"array","items":{"type":"string"}}},"required":["slides"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/app/onboarding/{slide}":{"patch":{"operationId":"mobileApp.updateSlide","summary":"PATCH /manage/app/onboarding/{slide} \u2014 edit a slide","tags":["Settings & Configuration"],"parameters":[{"name":"slide","in":"path","required":true,"description":"The slide ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"body":{"type":["string","null"]},"body_ar":{"type":["string","null"]},"image":{"type":["string","null"]},"image_url":{"type":["string","null"]},"sort_order":{"type":"integer"},"is_active":{"type":"boolean"}},"required":["id","title","title_ar","body","body_ar","image","image_url","sort_order","is_active"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"mobileApp.destroySlide","summary":"DELETE /manage/app/onboarding/{slide} \u2014 remove a slide","tags":["Settings & Configuration"],"parameters":[{"name":"slide","in":"path","required":true,"description":"The slide ID","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/app/versions":{"get":{"operationId":"mobileApp.versions","summary":"GET /manage/app/versions \u2014 both platforms (a row each, defaulted when unset)","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"versions":{"type":"array","items":{}}},"required":["versions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/app/versions/{platform}":{"put":{"operationId":"mobileApp.upsertVersion","summary":"PUT /manage/app/versions/{platform} \u2014 set the policy for ios|android","tags":["Settings & Configuration"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"latest_version":{"type":["string","null"],"maxLength":20},"min_version":{"type":["string","null"],"maxLength":20},"store_url":{"type":["string","null"],"format":"uri","maxLength":300},"soft_message":{"type":["string","null"],"maxLength":200},"soft_message_ar":{"type":["string","null"],"maxLength":200},"force_message":{"type":["string","null"],"maxLength":200},"force_message_ar":{"type":["string","null"],"maxLength":200},"is_active":{"type":["boolean","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"latest_version":{"type":"string"},"min_version":{"type":"string"},"store_url":{"type":"string"},"soft_message":{"type":"string"},"soft_message_ar":{"type":"string"},"force_message":{"type":"string"},"force_message_ar":{"type":"string"},"is_active":{"type":"boolean"}},"required":["platform","latest_version","min_version","store_url","soft_message","soft_message_ar","force_message","force_message_ar","is_active"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/ai-settings":{"get":{"operationId":"aiSetting.show","summary":"GET /manage/ai-settings \u2014 the current config (key masked)","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"has_key":{"type":"string"},"model":{"anyOf":[{"type":"string"},{"type":"string","enum":["claude-haiku-4-5"]}]},"is_active":{"type":"boolean"},"has_gemini_key":{"type":"string"},"voice":{"anyOf":[{"type":"string"},{"type":"string","enum":["Kore"]}]}},"required":["has_key","model","is_active","has_gemini_key","voice"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"aiSetting.update","summary":"PUT /manage/ai-settings \u2014 set the key / model. A blank key leaves the stored one intact","tags":["Settings & Configuration"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"anthropic_key":{"type":["string","null"],"maxLength":300},"model":{"type":["string","null"],"maxLength":60},"is_active":{"type":["boolean","null"]},"gemini_key":{"type":["string","null"],"maxLength":300},"voice":{"type":["string","null"],"maxLength":40}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"has_key":{"type":"string"},"model":{"anyOf":[{"type":"string"},{"type":"string","enum":["claude-haiku-4-5"]}]},"is_active":{"type":"boolean"},"has_gemini_key":{"type":"string"},"voice":{"anyOf":[{"type":"string"},{"type":"string","enum":["Kore"]}]}},"required":["has_key","model","is_active","has_gemini_key","voice"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/ai-settings/test":{"post":{"operationId":"aiSetting.test","summary":"POST /manage/ai-settings/test \u2014 verify the key with a tiny translation call","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"string"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/push-settings":{"get":{"operationId":"pushSetting.show","summary":"GET /manage/push-settings \u2014 the current config (credentials masked)","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"string"},"project_id":{"type":"string"},"client_email":{"type":"string"},"is_active":{"type":"boolean"},"device_count":{"type":"integer","minimum":0}},"required":["configured","project_id","client_email","is_active","device_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"pushSetting.update","summary":"PUT /manage/push-settings \u2014 upload/replace the service-account JSON, or just toggle active.\nA blank service_account leaves the stored credentials intact (so a save that only flips the\ntoggle keeps the key). The JSON is validated to be a real service account before storing","tags":["Settings & Configuration"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"service_account":{"type":["string","null"]},"is_active":{"type":["boolean","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"string"},"project_id":{"type":"string"},"client_email":{"type":"string"},"is_active":{"type":"boolean"},"device_count":{"type":"integer","minimum":0}},"required":["configured","project_id","client_email","is_active","device_count"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"pushSetting.destroy","summary":"DELETE /manage/push-settings \u2014 remove the brand's Firebase credentials","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"configured":{"type":"string"},"project_id":{"type":"string"},"client_email":{"type":"string"},"is_active":{"type":"boolean"},"device_count":{"type":"integer","minimum":0}},"required":["configured","project_id","client_email","is_active","device_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/push-settings/test":{"post":{"operationId":"pushSetting.test","summary":"POST /manage/push-settings/test \u2014 verify the credentials mint a Google token. If a device token is\nsupplied, also send a real test push to it. Returns { ok, sent? }","tags":["Settings & Configuration"],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":["string","null"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"ok":{"type":"boolean"},"sent":{"type":"string"},"delivery_error":{"type":["string","null"]}},"required":["ok","sent","delivery_error"]},{"type":"object","properties":{"ok":{"type":"boolean"},"error":{"type":"string","const":"not_configured"}},"required":["ok","error"]}]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/notification-settings":{"get":{"operationId":"notificationSetting.show","summary":"GET /manage/notification-settings \u2014 the catalogue merged with this brand's on/off state","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"notificationSetting.update","summary":"PUT /manage/notification-settings \u2014 upsert one or more toggles. Unknown keys are ignored","tags":["Settings & Configuration"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"audience":{"type":"string","enum":["member","trainer"]},"key":{"type":"string","maxLength":60},"enabled":{"type":"boolean"}},"required":["audience","key","enabled"]},"minItems":1}},"required":["items"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/workout-settings":{"get":{"operationId":"workoutWodAdmin.settings","summary":"GET /manage/workout-settings \u2014 the brand's default weight unit (kg | lb)","tags":["Settings & Configuration"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"weight_unit":{"type":"string"}},"required":["weight_unit"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"workoutWodAdmin.updateSettings","summary":"PUT /manage/workout-settings {weight_unit} \u2014 set the brand default","tags":["Settings & Configuration"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"weight_unit":{"type":"string","enum":["kg","lb"]}},"required":["weight_unit"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"weight_unit":{"type":"string"}},"required":["weight_unit"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/staff/invite":{"get":{"operationId":"staffInvite.show","summary":"Preview the invitation (name + email + brand) for the set-password screen","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"brand":{"type":"string"}},"required":["name","email","brand"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/staff/invite/accept":{"post":{"operationId":"staffInvite.accept","summary":"Set the password and activate the account","tags":["Team & Staff"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"password":{"type":"string","minLength":8},"password_confirmation":{"type":"string","minLength":8}},"required":["token","password","password_confirmation"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"activated":{"type":"boolean"},"email":{"type":"string"}},"required":["activated","email"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"}}}},"/staff/users":{"get":{"operationId":"staffUser.index","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"users":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"}},"required":["name","label"]}},"employee":{"type":["string","null"]},"last_login_at":{"type":"string"},"invited_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","name","email","status","roles","employee","last_login_at","invited_at","created_at"]}}},"required":["users"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"staffUser.store","summary":"Invite a teammate by email \u2014 creates an `invited` account and emails a set-password link","tags":["Team & Staff"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"email":{"type":"string","format":"email","maxLength":190},"role_names":{"type":"array","items":{"type":"string","enum":[""]}}},"required":["name","email"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"}},"required":["name","label"]}},"employee":{"type":["string","null"]},"last_login_at":{"type":"string"},"invited_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","name","email","status","roles","employee","last_login_at","invited_at","created_at"]}},"required":["user"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/staff/users/bulk":{"post":{"operationId":"staffUser.bulk","summary":"Bulk role-assign / status-change from the table's selection bar","tags":["Team & Staff"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"action":{"type":"string","enum":["assign_role","status"]},"role_name":{"type":"string","enum":[""]},"status":{"type":"string","enum":["active","suspended"]},"ids":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["action","ids"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"affected":{"type":"integer"}},"required":["affected"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/staff/users/{user}":{"patch":{"operationId":"staffUser.update","summary":"Edit a teammate: rename, change assigned roles, or suspend/re-activate","tags":["Team & Staff"],"parameters":[{"name":"user","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":120},"status":{"type":"string","enum":["active","suspended"]},"role_names":{"type":"array","items":{"type":"string","enum":[""]}}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"}},"required":["name","label"]}},"employee":{"type":["string","null"]},"last_login_at":{"type":"string"},"invited_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","name","email","status","roles","employee","last_login_at","invited_at","created_at"]}},"required":["user"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"staffUser.destroy","summary":"Remove a teammate's console access entirely","tags":["Team & Staff"],"parameters":[{"name":"user","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}},"required":["deleted"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"You can't delete your own account."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/staff/users/{user}/resend-invite":{"post":{"operationId":"staffUser.resendInvite","summary":"Re-send the invitation email (refreshes the token + expiry). Invited accounts only","tags":["Team & Staff"],"parameters":[{"name":"user","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sent":{"type":"boolean"}},"required":["sent"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"This account has already been activated."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/staff/roles":{"get":{"operationId":"staffUser.roles","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"roles":{"type":"string"}},"required":["roles"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/staff/permissions":{"get":{"operationId":"staffUser.permissions","summary":"The atomic permission catalogue (what each preset can grant) \u2014 minus the platform layer","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"permissions":{"type":"object","additionalProperties":{}}},"required":["permissions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/employees/{employee}/invite":{"post":{"operationId":"employeeAdmin.invite","summary":"POST /manage/employees/{employee}/invite \u2014 give an existing employee a portal login","tags":["Team & Staff"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","maxLength":190},"role_names":{"type":"array","items":{"type":"string","enum":[""]}}},"required":["email"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"people":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"string"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"string"},"monthly_sales_target_minor":{"type":"string"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]},{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"null"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"null"},"club_id":{"type":"null"},"club_name":{"type":"null"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"null"},"monthly_sales_target_minor":{"type":"null"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]}]}},"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"}},"required":["name","label"]}},"tiers":{"type":"array","prefixItems":[{"type":"string","const":"Sales"},{"type":"string","const":"Customer Service"},{"type":"string","const":"Coach"},{"type":"string","const":"Manager"},{"type":"string","const":"Reception"},{"type":"string","const":"Other"}],"minItems":6,"maxItems":6,"additionalItems":false}},"required":["people","clubs","roles","tiers"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/employees/{employee}/login":{"delete":{"operationId":"employeeAdmin.removeLogin","summary":"DELETE /manage/employees/{employee}/login \u2014 revoke an employee's portal login (keeps the employee)","tags":["Team & Staff"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"people":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"string"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"string"},"monthly_sales_target_minor":{"type":"string"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]},{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"null"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"null"},"club_id":{"type":"null"},"club_name":{"type":"null"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"null"},"monthly_sales_target_minor":{"type":"null"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]}]}},"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"}},"required":["name","label"]}},"tiers":{"type":"array","prefixItems":[{"type":"string","const":"Sales"},{"type":"string","const":"Customer Service"},{"type":"string","const":"Coach"},{"type":"string","const":"Manager"},{"type":"string","const":"Reception"},{"type":"string","const":"Other"}],"minItems":6,"maxItems":6,"additionalItems":false}},"required":["people","clubs","roles","tiers"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"You can't remove your own login."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/employees":{"get":{"operationId":"employeeAdmin.index","summary":"GET /manage/employees \u2014 the unified TEAM: one row per person, merging employees (sellers, with role\n+ branch + optional login) and login-only users (a portal account with no employee record). Carries\nthe form options (clubs, assignable roles, role tiers). This is the single source for the Team page","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"people":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"string"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"string"},"monthly_sales_target_minor":{"type":"string"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]},{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"null"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"null"},"club_id":{"type":"null"},"club_name":{"type":"null"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"null"},"monthly_sales_target_minor":{"type":"null"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]}]}},"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"}},"required":["name","label"]}},"tiers":{"type":"array","prefixItems":[{"type":"string","const":"Sales"},{"type":"string","const":"Customer Service"},{"type":"string","const":"Coach"},{"type":"string","const":"Manager"},{"type":"string","const":"Reception"},{"type":"string","const":"Other"}],"minItems":6,"maxItems":6,"additionalItems":false}},"required":["people","clubs","roles","tiers"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"employeeAdmin.store","summary":"POST /manage/employees \u2014 add an employee, optionally inviting a portal login","tags":["Team & Staff"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","maxLength":120},"last_name":{"type":["string","null"],"maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"hr_tier":{"type":"string","maxLength":60},"gender":{"$ref":"#/components/schemas/Gender"},"national_id":{"type":["string","null"],"maxLength":20},"club_id":{"type":["string","null"]},"is_active":{"type":"boolean"},"is_seller":{"type":"boolean"},"monthly_deal_target":{"type":["integer","null"],"description":"Monthly sales targets (compared against actuals on the leaderboard).","minimum":0,"maximum":100000},"monthly_sales_target_minor":{"type":["integer","null"],"minimum":0},"invite_login":{"type":"boolean","description":"A brand-new login to invite \u2026"},"email":{"type":["string","null"],"format":"email","maxLength":190},"user_id":{"type":["integer","null"],"description":"\u2026 OR an existing login to link (turn a login-only user into a seller). users.id is an int PK."},"role_names":{"type":"array","items":{"type":"string","enum":[""]}}},"required":["first_name","hr_tier"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"people":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"string"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"string"},"monthly_sales_target_minor":{"type":"string"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]},{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"null"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"null"},"club_id":{"type":"null"},"club_name":{"type":"null"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"null"},"monthly_sales_target_minor":{"type":"null"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]}]}},"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"}},"required":["name","label"]}},"tiers":{"type":"array","prefixItems":[{"type":"string","const":"Sales"},{"type":"string","const":"Customer Service"},{"type":"string","const":"Coach"},{"type":"string","const":"Manager"},{"type":"string","const":"Reception"},{"type":"string","const":"Other"}],"minItems":6,"maxItems":6,"additionalItems":false}},"required":["people","clubs","roles","tiers"]}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Managing logins requires the Users & Roles permission."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/employees/{employee}":{"patch":{"operationId":"employeeAdmin.update","summary":"PATCH /manage/employees/{employee} \u2014 edit an employee (name, role, branch, active)","tags":["Team & Staff"],"parameters":[{"name":"employee","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","maxLength":120},"last_name":{"type":["string","null"],"maxLength":120},"first_name_ar":{"type":["string","null"],"maxLength":120},"last_name_ar":{"type":["string","null"],"maxLength":120},"hr_tier":{"type":"string","maxLength":60},"club_id":{"type":["string","null"]},"is_active":{"type":"boolean"},"is_seller":{"type":"boolean"},"monthly_deal_target":{"type":["integer","null"],"minimum":0,"maximum":100000},"monthly_sales_target_minor":{"type":["integer","null"],"minimum":0}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"people":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"string"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"string"},"club_id":{"type":"string"},"club_name":{"type":"string"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"string"},"monthly_sales_target_minor":{"type":"string"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]},{"type":"object","properties":{"key":{"type":"string"},"employee_id":{"type":"null"},"user_id":{"type":"string"},"name":{"type":"string"},"hr_tier":{"type":"null"},"club_id":{"type":"null"},"club_name":{"type":"null"},"is_seller":{"type":"boolean"},"is_trainer":{"type":"boolean"},"is_active":{"type":"boolean"},"monthly_deal_target":{"type":"null"},"monthly_sales_target_minor":{"type":"null"},"login":{"type":["object","null"],"properties":{"user_id":{"type":"integer"},"email":{"type":"string"},"status":{"type":"string"},"roles":{"type":"object","additionalProperties":{}}},"required":["user_id","email","status","roles"]}},"required":["key","employee_id","user_id","name","hr_tier","club_id","club_name","is_seller","is_trainer","is_active","monthly_deal_target","monthly_sales_target_minor","login"]}]}},"clubs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"}},"required":["id","name"]}},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"label":{"type":"string"}},"required":["name","label"]}},"tiers":{"type":"array","prefixItems":[{"type":"string","const":"Sales"},{"type":"string","const":"Customer Service"},{"type":"string","const":"Coach"},{"type":"string","const":"Manager"},{"type":"string","const":"Reception"},{"type":"string","const":"Other"}],"minItems":6,"maxItems":6,"additionalItems":false}},"required":["people","clubs","roles","tiers"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/tickets":{"get":{"operationId":"manageTicket.index","summary":"GET /manage/tickets \u2014 the paginated queue with filters + an open-count badge","tags":["Team & Staff"],"parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"category","in":"query","schema":{"type":"string"}},{"name":"priority","in":"query","schema":{"type":"string"}},{"name":"assignee","in":"query","schema":{"type":"string"}},{"name":"club_id","in":"query","description":"employee id, or \"unassigned\"","schema":{"type":"string"}},{"name":"q","in":"query","schema":{"type":"string","maxLength":100}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":100}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ticket_number":{"type":"string"},"category":{"type":"string"},"priority":{"type":"string"},"status":{"type":"string"},"subject":{"type":"string"},"raised_by":{"type":["string","null"]},"raised_by_employee_id":{"type":"string"},"assigned_to":{"type":["string","null"]},"assigned_to_employee_id":{"type":["string","null"]},"club_id":{"type":["string","null"]},"due_at":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","ticket_number","category","priority","status","subject","raised_by","raised_by_employee_id","assigned_to","assigned_to_employee_id","club_id","due_at","resolved_at","created_at"]}},"meta":{"type":"object","properties":{"page":{"type":"integer"},"per_page":{"type":"integer"},"total":{"type":"string"},"last_page":{"type":["object","null"]}},"required":["page","per_page","total","last_page"]},"stats":{"type":"object","properties":{"open":{"type":"integer"},"unassigned":{"type":"integer"},"in_progress":{"type":"integer"},"resolved":{"type":"integer"},"total":{"type":"integer"}},"required":["open","unassigned","in_progress","resolved","total"]}},"required":["tickets","meta","stats"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/tickets/stats":{"get":{"operationId":"manageTicket.stats","summary":"GET /manage/tickets/stats \u2014 status counts for the dashboard tiles","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"open":{"type":"integer"},"unassigned":{"type":"integer"},"in_progress":{"type":"integer"},"resolved":{"type":"integer"},"total":{"type":"integer"}},"required":["open","unassigned","in_progress","resolved","total"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/tickets/{ticket}":{"get":{"operationId":"manageTicket.show","summary":"GET /manage/tickets/{ticket} \u2014 the full ticket + thread (staff see internal notes)","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"patch":{"operationId":"manageTicket.update","summary":"PATCH /manage/tickets/{ticket} \u2014 change priority / category / due date / status","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"priority":{"type":"string","enum":["low","normal","high","urgent"]},"category":{"type":"string","enum":["complaint","maintenance","it","facility","hr","other"]},"due_at":{"type":["string","null"],"format":"date-time"},"status":{"type":"string","enum":["open","in_progress","on_hold","resolved","closed","cancelled"]}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/tickets/{ticket}/assign":{"post":{"operationId":"manageTicket.assign","summary":"POST /manage/tickets/{ticket}/assign \u2014 assign to an employee (and start progress)","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"employee_id":{"type":"string"}},"required":["employee_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/tickets/{ticket}/resolve":{"post":{"operationId":"manageTicket.resolve","summary":"POST /manage/tickets/{ticket}/resolve \u2014 close it out with an optional resolution note","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":["string","null"],"maxLength":2000}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/tickets/{ticket}/comments":{"post":{"operationId":"manageTicket.comment","summary":"POST /manage/tickets/{ticket}/comments \u2014 a staff reply/note (no employee record required)","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","maxLength":2000},"internal":{"type":"boolean"}},"required":["body"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tickets/meta":{"get":{"operationId":"ticket.meta","summary":"GET /tickets/meta \u2014 the option lists for the raise form (categories / priorities / statuses)","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"categories":{"type":"array","prefixItems":[{"type":"string","const":"complaint"},{"type":"string","const":"maintenance"},{"type":"string","const":"it"},{"type":"string","const":"facility"},{"type":"string","const":"hr"},{"type":"string","const":"other"}],"minItems":6,"maxItems":6,"additionalItems":false},"priorities":{"type":"array","prefixItems":[{"type":"string","const":"low"},{"type":"string","const":"normal"},{"type":"string","const":"high"},{"type":"string","const":"urgent"}],"minItems":4,"maxItems":4,"additionalItems":false},"statuses":{"type":"array","prefixItems":[{"type":"string","const":"open"},{"type":"string","const":"in_progress"},{"type":"string","const":"on_hold"},{"type":"string","const":"resolved"},{"type":"string","const":"closed"},{"type":"string","const":"cancelled"}],"minItems":6,"maxItems":6,"additionalItems":false}},"required":["categories","priorities","statuses"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tickets":{"post":{"operationId":"ticket.store","summary":"POST /tickets \u2014 raise a ticket","tags":["Team & Staff"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"category":{"type":"string","enum":["complaint","maintenance","it","facility","hr","other"]},"priority":{"type":"string","enum":["low","normal","high","urgent"]},"subject":{"type":"string","maxLength":160},"description":{"type":"string","maxLength":4000},"club_id":{"type":["string","null"]},"image":{"type":["string","null"],"maxLength":5600000}},"required":["category","subject","description"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tickets/mine":{"get":{"operationId":"ticket.mine","summary":"GET /tickets/mine?scope=raised|assigned&status= \u2014 the employee's own tickets","tags":["Team & Staff"],"parameters":[{"name":"scope","in":"query","schema":{"type":"string","enum":["raised","assigned"]}},{"name":"status","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ticket_number":{"type":"string"},"category":{"type":"string"},"priority":{"type":"string"},"status":{"type":"string"},"subject":{"type":"string"},"raised_by":{"type":["string","null"]},"raised_by_employee_id":{"type":"string"},"assigned_to":{"type":["string","null"]},"assigned_to_employee_id":{"type":["string","null"]},"club_id":{"type":["string","null"]},"due_at":{"type":"string"},"resolved_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","ticket_number","category","priority","status","subject","raised_by","raised_by_employee_id","assigned_to","assigned_to_employee_id","club_id","due_at","resolved_at","created_at"]}}},"required":["tickets"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tickets/{ticket}":{"get":{"operationId":"ticket.show","summary":"GET /tickets/{ticket} \u2014 full ticket with its thread (internal notes hidden from the requester)","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"This ticket is not yours."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tickets/{ticket}/comments":{"post":{"operationId":"ticket.comment","summary":"POST /tickets/{ticket}/comments \u2014 reply on the thread","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","maxLength":2000},"internal":{"type":"boolean"}},"required":["body"]}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"This ticket is not yours."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/tickets/{ticket}/cancel":{"post":{"operationId":"ticket.cancel","summary":"POST /tickets/{ticket}/cancel \u2014 the requester withdraws their own open ticket","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{}}}}},"403":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"Only the requester can cancel."}},"required":["message"]}}}},"422":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"This ticket is already closed."}},"required":["message"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/support/tickets":{"get":{"operationId":"supportTicketAdmin.index","summary":"The brand ticket queue, newest first, optionally filtered by status/category","tags":["Team & Staff"],"parameters":[{"name":"status","in":"query","schema":{"type":["string","null"],"enum":["open","pending","resolved"]}},{"name":"category","in":"query","schema":{"type":["string","null"],"enum":["billing","membership","technical","other"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"member_name":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","member_name","subject","category","status","created_at","updated_at","messages"]}}},"required":["tickets"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/support/tickets/{ticket}":{"get":{"operationId":"supportTicketAdmin.show","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"member_name":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","member_name","subject","category","status","created_at","updated_at","messages"]}},"required":["ticket"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/support/tickets/{ticket}/reply":{"post":{"operationId":"supportTicketAdmin.reply","summary":"Append an agent reply to the thread","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"body":{"type":"string","maxLength":5000}},"required":["body"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"member_name":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","member_name","subject","category","status","created_at","updated_at","messages"]}},"required":["ticket"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/support/tickets/{ticket}/status":{"patch":{"operationId":"supportTicketAdmin.updateStatus","summary":"Move a ticket between open / pending / resolved","tags":["Team & Staff"],"parameters":[{"name":"ticket","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["open","pending","resolved"]}},"required":["status"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ticket":{"type":"object","properties":{"id":{"type":"string"},"number":{"type":"string"},"member_name":{"type":"string"},"subject":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"},"updated_at":{"type":"string"},"messages":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"sender":{"type":"string"},"body":{"type":"string"},"created_at":{"type":"string"}},"required":["id","sender","body","created_at"]}}},"required":["id","number","member_name","subject","category","status","created_at","updated_at","messages"]}},"required":["ticket"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/support/channels":{"get":{"operationId":"supportContent.channelsIndex","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"label":{"type":"string"},"label_ar":{"type":["string","null"]},"value":{"type":"string"},"availability":{"type":["string","null"]},"availability_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","type","label","label_ar","value","availability","availability_ar","sort_order"]}}},"required":["channels"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"supportContent.channelsStore","tags":["Team & Staff"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"label":{"type":"string"},"label_ar":{"type":["string","null"]},"value":{"type":"string"},"availability":{"type":["string","null"]},"availability_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","type","label","label_ar","value","availability","availability_ar","sort_order"]}},"required":["channel"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/support/channels/{channel}":{"patch":{"operationId":"supportContent.channelsUpdate","tags":["Team & Staff"],"parameters":[{"name":"channel","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"label":{"type":"string"},"label_ar":{"type":["string","null"]},"value":{"type":"string"},"availability":{"type":["string","null"]},"availability_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","type","label","label_ar","value","availability","availability_ar","sort_order"]}},"required":["channel"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"supportContent.channelsDestroy","tags":["Team & Staff"],"parameters":[{"name":"channel","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/support/faqs":{"get":{"operationId":"supportContent.faqsIndex","tags":["Team & Staff"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"faqs":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"question_ar":{"type":["string","null"]},"answer":{"type":"string"},"answer_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","question","question_ar","answer","answer_ar","sort_order"]}}},"required":["faqs"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"supportContent.faqsStore","tags":["Team & Staff"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"faq":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"question_ar":{"type":["string","null"]},"answer":{"type":"string"},"answer_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","question","question_ar","answer","answer_ar","sort_order"]}},"required":["faq"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/support/faqs/{faq}":{"patch":{"operationId":"supportContent.faqsUpdate","tags":["Team & Staff"],"parameters":[{"name":"faq","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"faq":{"type":"object","properties":{"id":{"type":"string"},"question":{"type":"string"},"question_ar":{"type":["string","null"]},"answer":{"type":"string"},"answer_ar":{"type":["string","null"]},"sort_order":{"type":"integer"}},"required":["id","question","question_ar","answer","answer_ar","sort_order"]}},"required":["faq"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"supportContent.faqsDestroy","tags":["Team & Staff"],"parameters":[{"name":"faq","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/me":{"get":{"operationId":"employee.me","summary":"GET /employee/me \u2014 the trainer profile + home club + today's floor duty","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"tier":{"type":["string","null"]},"club":{},"gender":{"type":"string"},"photo_url":{"type":["string","null"]},"initial":{"type":"string"},"floor_duty":{"type":"null"},"rating_avg":{"type":["number","null"],"description":"surfaced from the roster in Phase B"},"rating_count":{"type":"integer","minimum":0}},"required":["id","name","tier","club","gender","photo_url","initial","floor_duty","rating_avg","rating_count"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/today":{"get":{"operationId":"employee.today","summary":"GET /employee/today \u2014 the trainer's sessions starting today","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["sessions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/schedule":{"get":{"operationId":"employee.schedule","summary":"GET /employee/schedule \u2014 the trainer's upcoming sessions (next 14 days)","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"sessions":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["sessions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/roster":{"get":{"operationId":"employee.roster","summary":"GET /employee/roster \u2014 the trainer's next 7 days of sessions, grouped by day","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"string"}},"required":["days"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/occupancy":{"get":{"operationId":"employee.occupancy","summary":"GET /employee/occupancy \u2014 live-ish headcount per bookable zone (in-progress session bookings)","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"zones":{"type":"array","items":{"type":"object","properties":{"zone":{"type":"string"},"count":{"type":"integer","minimum":0},"capacity":{"type":"integer"}},"required":["zone","count","capacity"]}}},"required":["zones"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/sessions/{session}/conduct":{"post":{"operationId":"employee.conduct","summary":"POST /employee/sessions/{session}/conduct \u2014 attest the session as conducted (RFP 25 #3). Blocked\nunless the member has scanned into the facility today; conducting posts the trainer commission","tags":["Trainer App"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"conducted"}},"required":["status"]}}}},"422":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","const":"The member has not scanned into the facility today."}},"required":["message"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/sessions/{session}/no-show":{"post":{"operationId":"employee.noShow","summary":"POST /employee/sessions/{session}/no-show \u2014 the acting trainer marks the member(s) absent. A\nreserved PT credit is forfeited (no refund) and no commission is posted for a no-show","tags":["Trainer App"],"parameters":[{"name":"session","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/commissions":{"get":{"operationId":"employee.commissions","summary":"GET /employee/commissions \u2014 the trainer's commission ledger","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"earned_minor":{"type":"integer"},"pending_minor":{"type":"integer"},"clawback_minor":{"type":"integer"},"net_minor":{"type":"string"},"employment_type":{"type":"string"},"tier":{"type":["object","null"],"properties":{"name":{"type":"string"},"rate_percent":{"type":"string"},"commissionable_sessions_limit":{"type":"string"},"limit_period":{"type":"string"},"used_this_period":{"type":"integer","minimum":0}},"required":["name","rate_percent","commissionable_sessions_limit","limit_period","used_this_period"]},"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"source":{"type":"string"},"amount_minor":{"type":"integer"},"status":{"type":"string","enum":["earned","clawback","pending"]},"txn_id":{"type":"string"},"posted_at":{"type":"string"}},"required":["id","source","amount_minor","status","txn_id","posted_at"]}}},"required":["earned_minor","pending_minor","clawback_minor","net_minor","employment_type","tier","rows"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/availability":{"get":{"operationId":"employee.availability","summary":"GET /employee/availability \u2014 submitted windows per day with the change deadline + locks","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deadline_days":{"type":"integer","const":8},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"windows":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"locked":{"type":"boolean"}},"required":["date","windows","locked"]}}},"required":["deadline_days","days"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"employee.setAvailability","summary":"PUT /employee/availability \u2014 set the windows for a day (rejected if inside the deadline)","tags":["Trainer App"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string","format":"date-time"},"windows":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]}}},"required":["date","windows"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deadline_days":{"type":"integer","const":8},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"windows":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"locked":{"type":"boolean"}},"required":["date","windows","locked"]}}},"required":["deadline_days","days"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/availability/template":{"get":{"operationId":"employee.availabilityTemplate","summary":"GET /employee/availability/template \u2014 the trainer's weekly recurring pattern (client RFP UX)","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{},"deadline_days":{"type":"integer","const":8}},"required":["pattern","deadline_days"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"put":{"operationId":"employee.setAvailabilityTemplate","summary":"PUT /employee/availability/template \u2014 save the weekly pattern + fill dates beyond the change deadline","tags":["Trainer App"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"pattern":{"type":"array","items":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]}}}},"required":["pattern"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deadline_days":{"type":"integer","const":8},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"windows":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"string"},"minItems":0,"maxItems":0,"additionalItems":false}]},"locked":{"type":"boolean"}},"required":["date","windows","locked"]}}},"required":["deadline_days","days"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/notifications":{"get":{"operationId":"employee.notifications","summary":"GET /employee/notifications \u2014 the trainer's inbox","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"body":{"type":["string","null"]},"at":{"type":"string"},"read":{"type":"boolean"}},"required":["id","type","title","body","at","read"]}}},"required":["notifications"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/notifications/read-all":{"post":{"operationId":"employee.markAllNotificationsRead","summary":"POST /employee/notifications/read-all \u2014 mark every notification read","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"body":{"type":["string","null"]},"at":{"type":"string"},"read":{"type":"boolean"}},"required":["id","type","title","body","at","read"]}}},"required":["notifications"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/notifications/{notification}/read":{"post":{"operationId":"employee.markNotificationRead","summary":"POST /employee/notifications/{notification}/read \u2014 mark one read","tags":["Trainer App"],"parameters":[{"name":"notification","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"notifications":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"body":{"type":["string","null"]},"at":{"type":"string"},"read":{"type":"boolean"}},"required":["id","type","title","body","at","read"]}}},"required":["notifications"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/members":{"get":{"operationId":"employeeMember.index","summary":"GET /employee/members?q= \u2014 search members by name / mobile / national-id / agreement","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"mobile":{"type":["string","null"]},"email":{"type":["string","null"]},"national_id":{"type":["string","null"]},"agreement_no":{"type":"string"},"initial":{"type":"string"},"plan":{"anyOf":[{"type":"string"},{"type":"string","enum":["\u2014"]}]},"contract_ends_at":{"type":"string"},"pt_credits_remaining":{"type":"integer"},"pt_expires_at":{"type":"string"}},"required":["id","name","mobile","email","national_id","agreement_no","initial","plan","contract_ends_at","pt_credits_remaining","pt_expires_at"]}}},"required":["members"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/members/{member}":{"get":{"operationId":"employeeMember.show","summary":"GET /employee/members/{member} \u2014 the Member 360 (summary + workout + notes)","tags":["Trainer App"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/members/{member}/notes":{"post":{"operationId":"employeeMember.addNote","summary":"POST /employee/members/{member}/notes \u2014 add a trainer note","tags":["Trainer App"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","maxLength":1000}},"required":["text"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/members/{member}/workout":{"post":{"operationId":"employeeMember.logWorkout","summary":"POST /employee/members/{member}/workout \u2014 log a workout entry","tags":["Trainer App"],"parameters":[{"name":"member","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"exercise":{"type":"string","maxLength":120},"sets":{"type":"integer","minimum":0,"maximum":99},"reps":{"type":"integer","minimum":0,"maximum":999},"weight_kg":{"type":"number","minimum":0,"maximum":999}},"required":["exercise","sets","reps","weight_kg"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"string"}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/checklists":{"get":{"operationId":"employeeOps.checklists","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"checklists":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"title":{"type":"string"},"items":{"type":"array","items":{}},"last_completed_at":{"type":"string"}},"required":["id","kind","title","items","last_completed_at"]}}},"required":["checklists"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/checklists/{template}/submit":{"post":{"operationId":"employeeOps.submitChecklist","tags":["Trainer App"],"parameters":[{"name":"template","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"checklists":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"kind":{"type":"string"},"title":{"type":"string"},"items":{"type":"array","items":{}},"last_completed_at":{"type":"string"}},"required":["id","kind","title","items","last_completed_at"]}}},"required":["checklists"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/assets":{"get":{"operationId":"employeeOps.assets","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"assets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"zone":{"type":["string","null"]},"status":{"type":"string"}},"required":["id","code","name","zone","status"]}}},"required":["assets"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/tickets":{"get":{"operationId":"employeeOps.tickets","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"asset_id":{"type":"string"},"asset_name":{"type":"string"},"issue":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","asset_id","asset_name","issue","severity","status","created_at"]}}},"required":["tickets"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/assets/{asset}/report":{"post":{"operationId":"employeeOps.reportTicket","summary":"Reporting a fault marks the asset out-of-service (hidden from the member app, #9)","tags":["Trainer App"],"parameters":[{"name":"asset","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"issue":{"type":"string","maxLength":500},"severity":{"type":"string","enum":["low","medium","high"]}},"required":["issue","severity"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tickets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"asset_id":{"type":"string"},"asset_name":{"type":"string"},"issue":{"type":"string"},"severity":{"type":"string"},"status":{"type":"string"},"created_at":{"type":"string"}},"required":["id","asset_id","asset_name","issue","severity","status","created_at"]}}},"required":["tickets"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/gates":{"get":{"operationId":"employeeOps.gates","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"gates":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"zone":{"type":["string","null"]}},"required":["id","name","zone"]}}},"required":["gates"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/gate-log":{"get":{"operationId":"employeeOps.gateLog","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"log":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gate_id":{"type":"string"},"gate_name":{"type":"string"},"reason":{"type":"string"},"staff":{"type":"string"},"at":{"type":"string"}},"required":["id","gate_id","gate_name","reason","staff","at"]}}},"required":["log"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/gates/{gate}/open":{"post":{"operationId":"employeeOps.openGate","summary":"Every manual open is appended to the immutable override audit trail with staff + time (#11)","tags":["Trainer App"],"parameters":[{"name":"gate","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","enum":["member_assist","tailgate_fix","maintenance","delivery","emergency"]}},"required":["reason"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"log":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"gate_id":{"type":"string"},"gate_name":{"type":"string"},"reason":{"type":"string"},"staff":{"type":"string"},"at":{"type":"string"}},"required":["id","gate_id","gate_name","reason","staff","at"]}}},"required":["log"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/referral":{"get":{"operationId":"employeeOps.referral","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"url":{"type":"string"},"leads":{"type":"integer","minimum":0},"conversions":{"type":"integer","minimum":0},"commission_minor":{"type":"integer"}},"required":["code","url","leads","conversions","commission_minor"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/team":{"get":{"operationId":"employeeOps.team","tags":["Trainer App"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"team":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"initial":{"type":"string"},"on_leave":{"type":"boolean"}},"required":["id","name","initial","on_leave"]}}},"required":["team"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/team/{trainer}/bookings":{"get":{"operationId":"employeeOps.teamBookings","tags":["Trainer App"],"parameters":[{"name":"trainer","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"bookings":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"member":{"type":"string"},"title":{"type":"string"},"starts_at":{"type":"string"}},"required":["id","member","title","starts_at"]}}},"required":["bookings"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/employee/team/reassign":{"post":{"operationId":"employeeOps.reassign","summary":"Move every upcoming session from one trainer to another + notify the receiving trainer","tags":["Trainer App"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"from_id":{"type":"string"},"to_id":{"type":"string"}},"required":["from_id","to_id"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"moved":{"type":"integer"},"notified":{"type":"integer","enum":[1,0]}},"required":["moved","notified"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/webhooks/events":{"get":{"operationId":"webhookEvent.index","summary":"GET /webhooks/events \u2014 recent outbox events (all types), optional ?event_name= filter","tags":["Webhooks & Integrations"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event_name":{"type":"string"},"payload":{"type":"array","items":{}},"status":{"type":"string"},"attempts":{"type":"integer"},"dispatched_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","event_name","payload","status","attempts","dispatched_at","created_at"]}},"event_names":{"type":"array","description":"Distinct event names present, for a filter dropdown.","items":{}},"subscriptions":{"type":"array","description":"Configured HMAC-signed delivery endpoints (per event name).","items":{"type":"object","properties":{"id":{"type":"string"},"event_name":{"type":"string"},"url":{"type":"string"},"is_active":{"type":"boolean"}},"required":["id","event_name","url","is_active"]}}},"required":["events","event_names","subscriptions"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/webhooks/subscriptions":{"get":{"operationId":"webhookSubscription.index","tags":["Webhooks & Integrations"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"subscriptions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event_name":{"type":"string"},"event_names":{"type":["array","null"],"items":{}},"url":{"type":"string"},"secret":{"type":"string"},"headers":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","event_name","event_names","url","secret","headers","is_active"]}},"event_names":{"type":"array","prefixItems":[{"type":"string","const":"membership.contract.drafted"},{"type":"string","const":"membership.contract.activated"},{"type":"string","const":"membership.contract.renewed"},{"type":"string","const":"membership.contract.cancelled"},{"type":"string","const":"membership.contract.frozen"},{"type":"string","const":"membership.contract.unfrozen"},{"type":"string","const":"membership.contract.rewritten"},{"type":"string","const":"membership.contract.expired"},{"type":"string","const":"membership.contract.blocked"},{"type":"string","const":"membership.contract.unblocked"},{"type":"string","const":"membership.contract.plan_changed"},{"type":"string","const":"membership.reactivated"},{"type":"string","const":"membership.renewal.failed"},{"type":"string","const":"membership.grace.expired"},{"type":"string","const":"membership.agreement.generated"},{"type":"string","const":"membership.contract.cancellation_requested"},{"type":"string","const":"membership.contract.cancellation_withdrawn"},{"type":"string","const":"scheduling.booking.created"},{"type":"string","const":"scheduling.booking.cancelled"},{"type":"string","const":"scheduling.booking.attended"},{"type":"string","const":"scheduling.booking.no_show"},{"type":"string","const":"scheduling.session.conducted"},{"type":"string","const":"finance.payment.settled"},{"type":"string","const":"finance.refund.issued"},{"type":"string","const":"finance.retail.sale.completed"},{"type":"string","const":"finance.cash.variance_flagged"},{"type":"string","const":"identity.member.created"},{"type":"string","const":"facility.member.checked_in"},{"type":"string","const":"workouts.activity.recorded"}],"minItems":29,"maxItems":29,"additionalItems":false}},"required":["subscriptions","event_names"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"webhookSubscription.store","tags":["Webhooks & Integrations"],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"event_name":{"type":"string"},"event_names":{"type":["array","null"],"items":{}},"url":{"type":"string"},"secret":{"type":"string"},"headers":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","event_name","event_names","url","secret","headers","is_active"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/webhooks/subscriptions/{subscription}":{"patch":{"operationId":"webhookSubscription.update","tags":["Webhooks & Integrations"],"parameters":[{"name":"subscription","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"event_name":{"type":"string"},"event_names":{"type":["array","null"],"items":{}},"url":{"type":"string"},"secret":{"type":"string"},"headers":{"type":["array","null"],"items":{}},"is_active":{"type":"boolean"}},"required":["id","event_name","event_names","url","secret","headers","is_active"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"webhookSubscription.destroy","tags":["Webhooks & Integrations"],"parameters":[{"name":"subscription","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"No content"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/webhooks/relay":{"post":{"operationId":"webhookSubscription.relay","summary":"Deliver pending outbox events now (manual trigger; runs the same signed relay the cron does)","tags":["Webhooks & Integrations"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"processed":{"type":"integer"}},"required":["processed"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/webhooks/subscriptions/{subscription}/test":{"post":{"operationId":"webhookSubscription.test","summary":"POST /webhooks/subscriptions/{subscription}/test \u2014 fire a sample test.ping at the endpoint","tags":["Webhooks & Integrations"],"parameters":[{"name":"subscription","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"string"},"response_code":{"type":["integer","null"]}},"required":["ok","response_code"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/webhooks/subscriptions/{subscription}/deliveries":{"get":{"operationId":"webhookSubscription.deliveries","summary":"GET /webhooks/subscriptions/{subscription}/deliveries \u2014 the last 25 delivery attempts","tags":["Webhooks & Integrations"],"parameters":[{"name":"subscription","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"deliveries":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"event_name":{"type":"string"},"status":{"type":"string"},"attempts":{"type":"integer"},"response_code":{"type":["integer","null"]},"delivered_at":{"type":"string"},"created_at":{"type":"string"}},"required":["id","event_name","status","attempts","response_code","delivered_at","created_at"]}}},"required":["deliveries"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/webhooks/deliveries/{delivery}/redeliver":{"post":{"operationId":"webhookSubscription.redeliver","summary":"POST /webhooks/deliveries/{delivery}/redeliver \u2014 reset a delivery for the next relay pass","tags":["Webhooks & Integrations"],"parameters":[{"name":"delivery","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/mcp-tokens":{"get":{"operationId":"mcpToken.index","summary":"GET /manage/mcp-tokens \u2014 every connector token (never the secrets)","tags":["Webhooks & Integrations"],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"tokens":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"modules":{"type":["array","null"],"items":{}},"mask_pii":{"type":"boolean"},"allow_writes":{"type":"boolean"},"expires_at":{"type":"string"},"created_at":{"type":"string"},"last_used_at":{"type":"string"}},"required":["id","name","modules","mask_pii","allow_writes","expires_at","created_at","last_used_at"]}}]}},"required":["tokens"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"post":{"operationId":"mcpToken.store","summary":"POST /manage/mcp-tokens \u2014 mint a named, module-scoped token (plaintext returned once)","tags":["Webhooks & Integrations"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":40},"expires_days":{"type":["integer","null"],"minimum":1,"maximum":365},"mask_pii":{"type":["boolean","null"]},"allow_writes":{"type":["boolean","null"]},"modules":{"type":["array","null"],"description":"Assistant-module keys (validated for shape only \u2014 the agent service owns the registry;\nunknown keys simply match no tools). Empty / omitted = all modules.","items":{"type":"string","maxLength":40},"maxItems":30}},"required":["name"]}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"503":{"description":"An error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview.","example":"No brand in context."}},"required":["message"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}}},"/manage/mcp-tokens/{tokenId}":{"patch":{"operationId":"mcpToken.update","summary":"PATCH /manage/mcp-tokens/{tokenId} \u2014 re-scope an existing token (no re-mint needed)","tags":["Webhooks & Integrations"],"parameters":[{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"mask_pii":{"type":["boolean","null"]},"allow_writes":{"type":["boolean","null"]},"modules":{"type":["array","null"],"items":{"type":"string","maxLength":40},"maxItems":30}}}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"422":{"$ref":"#/components/responses/ValidationException"},"404":{"$ref":"#/components/responses/ModelNotFoundException"},"401":{"$ref":"#/components/responses/AuthenticationException"}}},"delete":{"operationId":"mcpToken.destroy","summary":"DELETE /manage/mcp-tokens/{tokenId} \u2014 revoke one token","tags":["Webhooks & Integrations"],"parameters":[{"name":"tokenId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]}}}},"401":{"$ref":"#/components/responses/AuthenticationException"}}}}},"components":{"schemas":{"ActivityType":{"type":"string","description":"Member activity / complaint ledger touchpoint type (RFP 12 #16). The activity ledger captures the service-relationship history for a Golden Record within a brand: complaints, welcome calls, tours, billing follow-ups and free-form notes.\n","enum":["complaint","welcome_call","tour","billing_followup","note","lead_captured","stage_change","assignment","escalation","free_visit","converted","win_back","contact","membership_created","membership_activated","membership_frozen","membership_unfrozen","membership_cancellation_requested","membership_cancellation_withdrawn","membership_cancelled","membership_transferred","membership_renewed","membership_plan_changed","membership_blocked","membership_unblocked","membership_expired"],"title":"ActivityType"},"AddOnKind":{"type":"string","description":"Class of add-on (RFP 02). `pt` carries session credits (personal training); `physical` is a one-off retail/accessory add-on with no credit ledger.\n","enum":["pt","physical"],"title":"AddOnKind"},"ApprovalStatus":{"type":"string","description":"Lifecycle of a maker-checker approval request (RFP 05). A request opens `Pending`; a checker drives it to a terminal `Approved` or `Rejected`.\n","enum":["pending","approved","rejected"],"title":"ApprovalStatus"},"BenefitType":{"type":"string","description":"How a staff member's {@see EmployeeBenefit} reduces the price of a plan (RFP 01 #8). `complimentary` grants the plan free of charge; `discount` applies a 0-100 percentage off (the `discount_percent`), e.g. a staff rate.\n","enum":["complimentary","discount"],"title":"BenefitType"},"BillingMode":{"type":"string","description":"How a plan is charged (RFP 01 #24). Monthly-recurring charges a non-refundable joining fee + first installment in month one, then installment-only.\n","enum":["paid_in_full","monthly_recurring"],"title":"BillingMode"},"BranchTarget":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"club_id":{"type":"string"},"period_month":{"type":"string","format":"date-time"},"new_count":{"type":"integer"},"renewal_count":{"type":"integer"},"amount_minor":{"type":"integer"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","brand_id","club_id","period_month","new_count","renewal_count","amount_minor","created_at","updated_at"],"title":"BranchTarget"},"CampaignChannel":{"type":"string","description":"Outbound marketing channel for a campaign (RFP 20 Marketing Automation).\n","enum":["email","sms","whatsapp"],"title":"CampaignChannel"},"CapPeriod":{"type":"string","description":"Rolling window a class-booking cap counts over (RFP-02 #29).\n","enum":["day","week","month"],"title":"CapPeriod"},"ClassWorkout":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"event_concept_id":{"type":["string","null"]},"title":{"type":"string"},"title_ar":{"type":["string","null"]},"notes":{"type":["string","null"]},"notes_ar":{"type":["string","null"]},"is_published":{"type":"boolean"},"created_by":{"type":["string","null"]},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","brand_id","event_concept_id","title","title_ar","notes","notes_ar","is_published","created_by","created_at","updated_at"],"title":"ClassWorkout"},"CoachCommissionPolicy":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"enabled":{"type":"boolean"},"ft_same_day_amount":{"type":"integer"},"ft_within_days":{"type":"integer"},"ft_within_amount":{"type":"integer"},"pt_attended_amount":{"type":"integer"},"pt_noshow_amount":{"type":"integer"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","brand_id","enabled","ft_same_day_amount","ft_within_days","ft_within_amount","pt_attended_amount","pt_noshow_amount","created_at","updated_at"],"title":"CoachCommissionPolicy"},"CouponDiscountType":{"type":"string","description":"How a retail {@see Coupon}'s `amount` is interpreted (RFP 07 retail discounts). `percentage` discounts a 0-100% share of the discountable base; `fixed` subtracts a fixed amount in minor units (halalas). Distinct from the membership DiscountType \u2014 coupons discount retail products / the retail cart, never membership plans.\n","enum":["percentage","fixed"],"title":"CouponDiscountType"},"CouponScope":{"type":"string","description":"What a retail {@see Coupon} discounts (RFP 07). `cart` applies to the whole retail cart subtotal; `products` applies only to the matching SKUs listed in the coupon's `product_ids` (the discountable base is the sum of those lines).\n","enum":["cart","products"],"title":"CouponScope"},"DiscountType":{"type":"string","description":"How a {@see Promotion}'s `amount` is interpreted (RFP 01 #10). `percentage` discounts a 0-100% share of the gross; `fixed` subtracts a fixed amount in minor units (halalas); `extension_days` grants `amount` bonus membership days instead of any money discount.\n","enum":["percentage","fixed","extension_days"],"title":"DiscountType"},"EventConcept":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"code":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"category":{"type":["string","null"]},"session_type":{"$ref":"#/components/schemas/SessionType"},"gender_restriction":{"$ref":"#/components/schemas/GenderDesignation"},"capacity":{"type":"integer"},"duration_minutes":{"type":"integer"},"intensity":{"type":["string","null"]},"commission_minor":{"type":"integer"},"currency":{"type":"string"},"is_active":{"type":"boolean"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"},"default_class_workout_id":{"type":["string","null"]},"image_url":{"type":["string","null"]},"ratings_enabled":{"type":"boolean"},"category_ar":{"type":["string","null"]}},"required":["id","brand_id","code","name","name_ar","category","session_type","gender_restriction","capacity","duration_minutes","intensity","commission_minor","currency","is_active","created_at","updated_at","default_class_workout_id","image_url","ratings_enabled","category_ar"],"title":"EventConcept"},"FeeTrigger":{"type":"string","description":"When a {@see FeeType} is applied (RFP 01 #26). `manual` fees are raised by staff on demand; the others fire off the matching membership lifecycle moment.\n","enum":["on_join","on_rewrite","on_freeze","manual"],"title":"FeeTrigger"},"FeeType":{"type":"string","description":"Category of a one-off membership fee (RFP 01 #26). Native to membership \u2014 these are the charge reasons HO configures (joining, contract rewrite, freeze, admin, replacement card, cancellation), independent of how they are later collected.\n","enum":["joining","rewrite","freeze","admin","replacement_card","cancellation"],"title":"FeeType"},"FitnessExercise":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"icon_key":{"type":["string","null"]},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"muscle_group":{"type":["string","null"]},"muscle_group_ar":{"type":["string","null"]},"equipment":{"type":["string","null"]},"equipment_ar":{"type":["string","null"]},"difficulty":{"type":"string"},"description":{"type":["string","null"]},"description_ar":{"type":["string","null"]},"sort_order":{"type":"integer"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","brand_id","icon_key","name","name_ar","muscle_group","muscle_group_ar","equipment","equipment_ar","difficulty","description","description_ar","sort_order","created_at","updated_at"],"title":"FitnessExercise"},"Gender":{"type":"string","description":"A person's gender. PDPL note: female members are subject to biometric zero-storage.\n","enum":["male","female"],"title":"Gender"},"GenderDesignation":{"type":"string","description":"Gender designation for a physical space (Club / Area). Drives the hard, non-overridable gender-segregation invariant enforced server-side across turnstiles, booking and POS (RFP Modules 01, 02, 09, 10, 16).\n","enum":["male","female","mixed","family"],"title":"GenderDesignation"},"GiftPassStatus":{"type":"string","description":"Lifecycle of a day-pass gift (RFP-01 #11). A member gifts a one-time free pass to a friend: it starts {@see self::Issued}, becomes {@see self::Redeemed} when the recipient claims it, or {@see self::Expired} once its window lapses.\n","enum":["issued","redeemed","expired"],"title":"GiftPassStatus"},"LockerStatus":{"type":"string","description":"Lifecycle state of a physical locker (RFP-12 #20, native locker management). `available` lockers can be assigned; `assigned` ones hold an open {@see LockerAssignment}; `maintenance` lockers are temporarily out of service and cannot be assigned.\n","enum":["available","assigned","maintenance"],"title":"LockerStatus"},"MemberSegment":{"type":"string","description":"Buyer segment used for price-point eligibility (RFP 01 #60).\n","enum":["new","renewal","reactivation","b2b"],"title":"MemberSegment"},"NearpayTransaction":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"terminal_id":{"type":["string","null"]},"reference":{"type":["string","null"]},"nearpay_uuid":{"type":["string","null"]},"scheme":{"type":["string","null"]},"card_last4":{"type":["string","null"]},"payment_id":{"type":["string","null"]},"member_id":{"type":["string","null"]},"captured_at":{"type":["string","null"],"format":"date-time"},"meta":{"type":["array","null"],"items":{}},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","brand_id","status","type","amount_minor","currency","terminal_id","reference","nearpay_uuid","scheme","card_last4","payment_id","member_id","captured_at","meta","created_at","updated_at"],"title":"NearpayTransaction"},"PipelineStage":{"type":"string","description":"Lead pipeline stages for the acquisition funnel (RFP Module 11 Lead Management): New \u2192 Contacted \u2192 Free visit booked \u2192 Visited (show) / No-show \u2192 2nd visit booked \u2192 Won (hot) / Lost (cold). The free-visit stages are driven by a scheduled free-visit appointment's attendance (app layer).\n","enum":["new","contacted","free_visit_booked","visited","no_show","second_visit_booked","won","lost"],"title":"PipelineStage"},"PlanFamily":{"type":"string","description":"Commercial shape of a membership plan (RFP 01 #5\u2013#8, #11, #12).\n","enum":["b2c","b2b","group","employee","day_pass","seasonal"],"title":"PlanFamily"},"PriceScopeType":{"type":"string","description":"Scope at which a price point applies (RFP 01 #59). Resolution precedence is narrow > broad: a facility price overrides a city price overrides a brand price overrides the network price. `rank()` encodes that ordering.\n","enum":["network","brand","country","region","branch_group","city","facility"],"title":"PriceScopeType"},"ReferralSource":{"type":"string","description":"How a member/lead came to the gym (acquisition source) \u2014 a profile classification distinct from the member-get-member Referral programme (RFP 19). Optional on every create path.\n","enum":["walk_in","inbound_lead","referral","social_media","campaign","event","other"],"title":"ReferralSource"},"SalesChannel":{"type":"string","description":"Channel a plan is sold through; drives per-channel visibility (RFP 01 #2).\n","enum":["pos","web","mobile","corporate_portal","partner","kiosk","call_center"],"title":"SalesChannel"},"SessionType":{"type":"string","description":"Drives the cancellation window and completion status (RFP 14-25): PT=Completed, GX=Confirmed.\n","enum":["pt","gx"],"title":"SessionType"},"SubscriptionStatus":{"type":"string","description":"Lifecycle of a tenant's platform subscription. `suspended` is a billing state on the subscription; the actual tenant access flag lives on Brand.is_active (suspend is flag-only at the platform layer).\n","enum":["trial","active","suspended","cancelled"],"title":"SubscriptionStatus"},"SupervisorCommissionPolicy":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"enabled":{"type":"boolean"},"personal_gate_new_sales":{"type":"integer"},"personal_band1_min":{"type":"integer"},"personal_band1_rate":{"type":"number"},"personal_band2_min":{"type":"integer"},"personal_band2_rate":{"type":"number"},"branch_gate_new_sales":{"type":"integer"},"branch_gate_renewal_rate":{"type":"number"},"branch_band1_min":{"type":"integer"},"branch_band1_rate":{"type":"number"},"branch_band2_min":{"type":"integer"},"branch_band2_rate":{"type":"number"},"renewal_band1_min_rate":{"type":"number"},"renewal_band1_rate":{"type":"number"},"renewal_band2_min_rate":{"type":"number"},"renewal_band2_rate":{"type":"number"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","brand_id","enabled","personal_gate_new_sales","personal_band1_min","personal_band1_rate","personal_band2_min","personal_band2_rate","branch_gate_new_sales","branch_gate_renewal_rate","branch_band1_min","branch_band1_rate","branch_band2_min","branch_band2_rate","renewal_band1_min_rate","renewal_band1_rate","renewal_band2_min_rate","renewal_band2_rate","created_at","updated_at"],"title":"SupervisorCommissionPolicy"},"TenantInvoiceStatus":{"type":"string","description":"Lifecycle of an invoice Memvera issues to a tenant (Brand). `due` on issue, `paid` once settled, `overdue` past the due date, `void` if cancelled. This is a PLATFORM-layer billing concept (Memvera billing the gym operator), not the brand's own member invoicing.\n","enum":["due","paid","overdue","void"],"title":"TenantInvoiceStatus"},"WorkoutLevel":{"type":"object","properties":{"id":{"type":"string"},"brand_id":{"type":"string"},"name":{"type":"string"},"name_ar":{"type":["string","null"]},"sort":{"type":"integer"},"is_default":{"type":"boolean"},"is_active":{"type":"boolean"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","brand_id","name","name_ar","sort","is_default","is_active","created_at","updated_at"],"title":"WorkoutLevel"}},"responses":{"ValidationException":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Errors overview."},"errors":{"type":"object","description":"A detailed description of each field that failed validation.","additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["message","errors"]}}}},"AuthenticationException":{"description":"Unauthenticated","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview."}},"required":["message"]}}}},"ModelNotFoundException":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview."}},"required":["message"]}}}}}}}