{"openapi":"3.1.0","info":{"title":"Kabom API","description":"USSD-first group finance API for community savings groups.","license":{"name":""},"version":"0.2.6"},"paths":{"/discover":{"get":{"tags":["Invites"],"operationId":"discover","parameters":[{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Discoverable public groups","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_GroupPreviewDto"}}}}},"security":[{}]}},"/discover/match":{"post":{"tags":["Invites"],"operationId":"discover_match","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchGroupRequest"}}},"required":true},"responses":{"200":{"description":"Public groups ranked by fit to the query","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoverListDto"}}}}},"security":[{}]}},"/discover/{group_id}/join":{"post":{"tags":["Invites"],"operationId":"request_join_public","parameters":[{"name":"group_id","in":"path","description":"Public group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinGroupRequest"}}},"required":true},"responses":{"201":{"description":"Join request created (pending approval)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinResultDto"}}}},"404":{"description":"Group not found or not public","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Already a member or pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]}},"/join/{code}":{"get":{"tags":["Invites"],"operationId":"preview","parameters":[{"name":"code","in":"path","description":"Group invite code","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Public group preview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupPreviewDto"}}}},"404":{"description":"Unknown invite code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]},"post":{"tags":["Invites"],"operationId":"request_join","parameters":[{"name":"code","in":"path","description":"Group invite code","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinGroupRequest"}}},"required":true},"responses":{"201":{"description":"Join request created (pending approval)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinResultDto"}}}},"404":{"description":"Unknown invite code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Already a member or pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]}},"/ussd":{"post":{"tags":["USSD"],"operationId":"handle","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UssdRequest"}}},"required":true},"responses":{"200":{"description":"USSD continuation; body is prefixed CON (continue) or END (terminal)"}},"security":[{}]}},"/v1/assistant/reminders/draft":{"post":{"tags":["Assistant"],"operationId":"draft_reminder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftReminderRequest"}}},"required":true},"responses":{"200":{"description":"Drafted reminder","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantTextDto"}}}}}}},"/v1/auth/accept-invite":{"post":{"tags":["Auth"],"operationId":"accept_invite","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInviteRequest"}}},"required":true},"responses":{"200":{"description":"Invite accepted; account activated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"description":"Invalid or expired code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]}},"/v1/auth/invite":{"post":{"tags":["Auth"],"operationId":"invite","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteAdminRequest"}}},"required":true},"responses":{"202":{"description":"Invite sent; verification code dispatched","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationRequiredResponse"}}}},"403":{"description":"Insufficient role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Email or phone already registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/auth/login":{"post":{"tags":["Auth"],"operationId":"login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"description":"Invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]}},"/v1/auth/logout":{"post":{"tags":["Auth"],"operationId":"logout","responses":{"204":{"description":"Session revoked"}}}},"/v1/auth/me":{"get":{"tags":["Auth"],"operationId":"me","responses":{"200":{"description":"Current admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDto"}}}}}}},"/v1/auth/refresh":{"post":{"tags":["Auth"],"operationId":"refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshRequest"}}},"required":true},"responses":{"200":{"description":"Rotated token pair","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"description":"Invalid refresh token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]}},"/v1/auth/resend-verification":{"post":{"tags":["Auth"],"operationId":"resend_verification","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendVerificationRequest"}}},"required":true},"responses":{"202":{"description":"Code re-sent if the account exists and is unverified"}},"security":[{}]}},"/v1/auth/signup":{"post":{"tags":["Auth"],"operationId":"signup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupRequest"}}},"required":true},"responses":{"202":{"description":"Account created; verification code sent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationRequiredResponse"}}}},"409":{"description":"Email or phone already registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]}},"/v1/auth/verify-signup":{"post":{"tags":["Auth"],"operationId":"verify_signup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifySignupRequest"}}},"required":true},"responses":{"200":{"description":"Account verified","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"}}}},"401":{"description":"Invalid or expired code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]}},"/v1/channels/admin-link-token":{"post":{"tags":["Member assistant"],"operationId":"create_admin_link_token","responses":{"200":{"description":"A one-time code to link a chat as an admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkTokenDto"}}}}}}},"/v1/contributions":{"post":{"tags":["Contributions"],"operationId":"initiate","parameters":[{"name":"Idempotency-Key","in":"header","description":"Idempotency key (UUID); required","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InitiateContributionRequest"}}},"required":true},"responses":{"201":{"description":"Collection initiated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContributionDto"}}}},"404":{"description":"Group or member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/contributions/{id}":{"get":{"tags":["Contributions"],"operationId":"get","parameters":[{"name":"id","in":"path","description":"Contribution ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Contribution","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContributionDto"}}}}}}},"/v1/dues/{id}":{"get":{"tags":["Dues"],"operationId":"get","parameters":[{"name":"id","in":"path","description":"Due ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Due","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DueDto"}}}}}}},"/v1/dues/{id}/close":{"post":{"tags":["Dues"],"operationId":"close","parameters":[{"name":"id","in":"path","description":"Due ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Due closed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DueDto"}}}}}}},"/v1/dues/{id}/status":{"get":{"tags":["Dues"],"operationId":"status","parameters":[{"name":"id","in":"path","description":"Due ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Per-member funding status for the due","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DueStatusView"}}}}}}},"/v1/expenses/{id}":{"get":{"tags":["Expenses"],"operationId":"get","parameters":[{"name":"id","in":"path","description":"Expense ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Expense","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseDto"}}}}}},"delete":{"tags":["Expenses"],"operationId":"remove","parameters":[{"name":"id","in":"path","description":"Expense ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Expense deleted"}}}},"/v1/groups":{"get":{"tags":["Groups"],"operationId":"list","parameters":[{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Groups in the org","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_GroupDto"}}}}}},"post":{"tags":["Groups"],"operationId":"create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGroupRequest"}}},"required":true},"responses":{"201":{"description":"Group created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupDto"}}}}}}},"/v1/groups/{group_id}/members":{"get":{"tags":["Members"],"operationId":"list","parameters":[{"name":"group_id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Members in the group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_MemberDto"}}}}}},"post":{"tags":["Members"],"operationId":"add","parameters":[{"name":"group_id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberRequest"}}},"required":true},"responses":{"201":{"description":"Member added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberDto"}}}},"400":{"description":"Invalid phone number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Member already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/groups/{id}":{"get":{"tags":["Groups"],"operationId":"get","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupDto"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"tags":["Groups"],"operationId":"archive","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Group archived"}}},"patch":{"tags":["Groups"],"operationId":"update","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGroupRequest"}}},"required":true},"responses":{"200":{"description":"Group updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupDto"}}}}}}},"/v1/groups/{id}/assistant/health-summary":{"post":{"tags":["Assistant"],"operationId":"health_summary","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Group health summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantTextDto"}}}}}}},"/v1/groups/{id}/contributions":{"get":{"tags":["Contributions"],"operationId":"list","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Contribution ledger","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ContributionDto"}}}}}}},"/v1/groups/{id}/cycle-status":{"get":{"tags":["Contributions"],"operationId":"cycle_status","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Current-cycle funding status by member","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CycleStatusDto"}}}}}}},"/v1/groups/{id}/dues":{"get":{"tags":["Dues"],"operationId":"list","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Dues for the group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_DueDto"}}}}}},"post":{"tags":["Dues"],"operationId":"create","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateDueRequest"}}},"required":true},"responses":{"201":{"description":"Due created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DueDto"}}}},"404":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/groups/{id}/expenses":{"get":{"tags":["Expenses"],"operationId":"list","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Expense ledger","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ExpenseDto"}}}}}},"post":{"tags":["Expenses"],"operationId":"record","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordExpenseRequest"}}},"required":true},"responses":{"201":{"description":"Expense recorded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpenseDto"}}}},"404":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/groups/{id}/join-requests":{"get":{"tags":["Invites"],"operationId":"list_pending","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Pending join requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_MemberDto"}}}}}}},"/v1/groups/{id}/payouts":{"get":{"tags":["Payouts"],"operationId":"list","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Payout history","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_PayoutDto"}}}}}}},"/v1/groups/{id}/reminder-drafts":{"get":{"tags":["Reminders"],"operationId":"list_pending","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Pending reminder drafts for the group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ReminderDraftDto"}}}},"403":{"description":"Insufficient role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/groups/{id}/reminders":{"post":{"tags":["Reminders"],"operationId":"send","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendRemindersRequest"}}},"required":true},"responses":{"202":{"description":"Reminders queued for unpaid members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RemindersQueuedDto"}}}},"404":{"description":"Group not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/groups/{id}/rotation":{"get":{"tags":["Groups"],"operationId":"rotation","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Rotation order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotationView"}}}}}}},"/v1/groups/{id}/scores":{"get":{"tags":["Trust"],"operationId":"group_scores","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Trust scores for the group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_TrustScoreDto"}}}}}}},"/v1/groups/{id}/sms":{"get":{"tags":["SMS"],"operationId":"list","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Outbound SMS log","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_SmsDto"}}}}}}},"/v1/groups/{id}/sms/test":{"post":{"tags":["SMS"],"operationId":"send_test","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendTestRequest"}}},"required":true},"responses":{"202":{"description":"Test SMS queued"}}}},"/v1/groups/{id}/transparency":{"get":{"tags":["Transparency"],"operationId":"summary","parameters":[{"name":"id","in":"path","description":"Group ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Group transparency summary (no member-identifying detail)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransparencyDto"}}}}}}},"/v1/member/assistant/audio/{id}":{"get":{"tags":["Member assistant"],"operationId":"audio","parameters":[{"name":"id","in":"path","description":"Stored audio ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Audio bytes","content":{"application/octet-stream":{}}},"404":{"description":"Audio not found or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/member/assistant/chat":{"post":{"tags":["Member assistant"],"operationId":"chat","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}},"required":true},"responses":{"200":{"description":"Assistant reply","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatResponse"}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/member/assistant/threads":{"get":{"tags":["Member assistant"],"operationId":"threads","parameters":[{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The member's conversation threads","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ThreadSummaryDto"}}}}}}},"/v1/member/assistant/threads/{thread_id}/messages":{"get":{"tags":["Member assistant"],"operationId":"thread_messages","parameters":[{"name":"thread_id","in":"path","description":"Conversation thread ID","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Messages in the thread","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_ThreadMessageDto"}}}},"404":{"description":"Thread not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/member/assistant/voice":{"post":{"tags":["Member assistant"],"operationId":"voice","responses":{"200":{"description":"Assistant reply to a voice note","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoiceReplyDto"}}}},"400":{"description":"Unsupported or oversized audio","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/member/auth/refresh":{"post":{"tags":["Member portal"],"operationId":"refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberRefreshRequest"}}},"required":true},"responses":{"200":{"description":"Refreshed member tokens","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberTokenResponse"}}}}},"security":[{}]}},"/v1/member/auth/request-otp":{"post":{"tags":["Member portal"],"operationId":"request_otp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RequestOtpRequest"}}},"required":true},"responses":{"202":{"description":"If the number belongs to a member, an OTP SMS is sent"}},"security":[{}]}},"/v1/member/auth/verify-otp":{"post":{"tags":["Member portal"],"operationId":"verify_otp","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyOtpRequest"}}},"required":true},"responses":{"200":{"description":"Member tokens issued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberTokenResponse"}}}},"401":{"description":"Invalid or expired code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}},"security":[{}]}},"/v1/member/channels/link-token":{"post":{"tags":["Member assistant"],"operationId":"create_link_token","responses":{"200":{"description":"A one-time code to link a messaging channel","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkTokenDto"}}}}}}},"/v1/member/contributions":{"post":{"tags":["Member portal"],"operationId":"self_contribution","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SelfContributionRequest"}}},"required":true},"responses":{"201":{"description":"Collection initiated for the member","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContributionDto"}}}},"404":{"description":"Not an active member of the group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/member/me":{"get":{"tags":["Member portal"],"operationId":"me","responses":{"200":{"description":"The member's groups and per-group status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}}}}},"/v1/members/{id}":{"get":{"tags":["Members"],"operationId":"get","parameters":[{"name":"id","in":"path","description":"Member ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Member","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberDto"}}}}}},"delete":{"tags":["Members"],"operationId":"remove","parameters":[{"name":"id","in":"path","description":"Member ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Member removed"}}},"patch":{"tags":["Members"],"operationId":"update","parameters":[{"name":"id","in":"path","description":"Member ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemberRequest"}}},"required":true},"responses":{"200":{"description":"Member updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberDto"}}}}}}},"/v1/members/{id}/approve":{"post":{"tags":["Invites"],"operationId":"approve","parameters":[{"name":"id","in":"path","description":"Member ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Member approved and activated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberDto"}}}},"404":{"description":"No pending member","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/members/{id}/reject":{"post":{"tags":["Invites"],"operationId":"reject","parameters":[{"name":"id","in":"path","description":"Member ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Join request rejected"}}}},"/v1/members/{id}/score":{"get":{"tags":["Trust"],"operationId":"member_score","parameters":[{"name":"id","in":"path","description":"Member ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Member trust score","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TrustScoreDto"}}}}}}},"/v1/notifications":{"get":{"tags":["Notifications"],"operationId":"list","parameters":[{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"The caller's notifications","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_NotificationDto"}}}}}}},"/v1/notifications/read-all":{"post":{"tags":["Notifications"],"operationId":"mark_all_read","responses":{"204":{"description":"All notifications marked read"}}}},"/v1/notifications/unread-count":{"get":{"tags":["Notifications"],"operationId":"unread_count","responses":{"200":{"description":"Unread notification count","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountDto"}}}}}}},"/v1/notifications/{id}":{"delete":{"tags":["Notifications"],"operationId":"remove","parameters":[{"name":"id","in":"path","description":"Notification ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Notification deleted"},"404":{"description":"Notification not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/notifications/{id}/read":{"post":{"tags":["Notifications"],"operationId":"mark_read","parameters":[{"name":"id","in":"path","description":"Notification ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Notification marked read","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationDto"}}}},"404":{"description":"Notification not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/notifications/{id}/unread":{"post":{"tags":["Notifications"],"operationId":"mark_unread","parameters":[{"name":"id","in":"path","description":"Notification ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Notification marked unread","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationDto"}}}},"404":{"description":"Notification not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/payouts":{"post":{"tags":["Payouts"],"operationId":"create","parameters":[{"name":"Idempotency-Key","in":"header","description":"Idempotency key (UUID); required","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePayoutRequest"}}},"required":true},"responses":{"201":{"description":"Disbursement triggered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoutDto"}}}},"409":{"description":"Payout already exists for this cycle","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/payouts/{id}":{"get":{"tags":["Payouts"],"operationId":"get","parameters":[{"name":"id","in":"path","description":"Payout ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Payout","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoutDto"}}}}}}},"/v1/reminder-drafts/{id}":{"patch":{"tags":["Reminders"],"operationId":"edit","parameters":[{"name":"id","in":"path","description":"Reminder draft ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditDraftRequest"}}},"required":true},"responses":{"200":{"description":"Updated draft","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReminderDraftDto"}}}},"404":{"description":"Draft not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Draft is no longer pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/reminder-drafts/{id}/approve":{"post":{"tags":["Reminders"],"operationId":"approve","parameters":[{"name":"id","in":"path","description":"Reminder draft ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Draft approved; sent now or shortly by the worker","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReminderDraftDto"}}}},"404":{"description":"Draft not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Draft is no longer pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/reminder-drafts/{id}/discard":{"post":{"tags":["Reminders"],"operationId":"discard","parameters":[{"name":"id","in":"path","description":"Reminder draft ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Draft discarded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReminderDraftDto"}}}},"404":{"description":"Draft not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Draft is no longer pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/users":{"get":{"tags":["Users"],"operationId":"list","parameters":[{"name":"page","in":"query","description":"Page number, 1-indexed. Default 1. Derives the offset from `size` unless\nan explicit `offset` is given.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"size","in":"query","description":"Page size. Default 50, clamped to [1, 200]. Alias of `limit`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"limit","in":"query","description":"Max rows to return. Default 50, clamped to [1, 200]. Legacy alias of `size`.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"offset","in":"query","description":"Rows to skip. Default derived from `page`/`size`, clamped to >= 0.","required":false,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","description":"Keyset cursor: return rows strictly older than this id. Ids are UUIDv7\n(time-ordered), so this walks newest-first without OFFSET. Endpoints that\nsupport keyset paging ignore `offset` when this is set.","required":false,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Admins in the org","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResponse_UserDto"}}}}}},"post":{"tags":["Users"],"operationId":"create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}},"required":true},"responses":{"201":{"description":"Admin created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"403":{"description":"Insufficient role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Email or phone already registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/users/{id}":{"get":{"tags":["Users"],"operationId":"get","parameters":[{"name":"id","in":"path","description":"Admin ID","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Admin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/users/{id}/role":{"patch":{"tags":["Users"],"operationId":"update_role","parameters":[{"name":"id","in":"path","description":"Admin ID","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRoleRequest"}}},"required":true},"responses":{"200":{"description":"Role updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserDto"}}}},"403":{"description":"Insufficient role","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/webhooks/moolre":{"post":{"tags":["Webhooks"],"operationId":"handle_moolre_webhook","requestBody":{"description":"Moolre webhook payload (shared-secret gated, then re-verified against the status endpoint)","content":{"text/plain":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"description":"Processed or duplicate redelivery"},"401":{"description":"Shared-secret verification failed"}},"security":[{}]}},"/webhooks/telegram":{"post":{"tags":["Webhooks"],"operationId":"receive","requestBody":{"description":"Telegram Bot API update (secret-token verified)","content":{"text/plain":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"description":"Processed or ignored"},"401":{"description":"Secret-token verification failed"}},"security":[{}]}},"/webhooks/whatsapp":{"get":{"tags":["Webhooks"],"operationId":"verify","responses":{"200":{"description":"Verification handshake echo"},"403":{"description":"Verification failed"}},"security":[{}]},"post":{"tags":["Webhooks"],"operationId":"receive","requestBody":{"description":"WhatsApp Cloud API webhook payload (HMAC-verified)","content":{"text/plain":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"description":"Processed or ignored"},"401":{"description":"Signature verification failed"}},"security":[{}]}}},"components":{"schemas":{"AcceptInviteRequest":{"type":"object","required":["code","password"],"properties":{"email":{"type":["string","null"],"description":"The email or `msisdn` the invite code was sent to."},"msisdn":{"type":["string","null"]},"code":{"type":"string"},"password":{"type":"string"}}},"AddMemberRequest":{"type":"object","required":["msisdn","full_name"],"properties":{"msisdn":{"type":"string"},"full_name":{"type":"string"},"network":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Network"}]}}},"AdminDto":{"type":"object","required":["id","org_id","full_name","role"],"properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]},"msisdn":{"type":["string","null"]},"full_name":{"type":"string"},"role":{"type":"string"}}},"AdminRole":{"type":"string","enum":["owner","admin","agent"]},"AssistantTextDto":{"type":"object","required":["text"],"properties":{"text":{"type":"string"}}},"CategorySpend":{"type":"object","required":["category","total"],"properties":{"category":{"type":"string"},"total":{"type":"string"}}},"ChatRequest":{"type":"object","required":["message"],"properties":{"message":{"type":"string"},"thread_id":{"type":["string","null"],"format":"uuid"},"language":{"type":["string","null"]}}},"ChatResponse":{"type":"object","required":["reply","thread_id","language"],"properties":{"reply":{"type":"string"},"thread_id":{"type":"string","format":"uuid"},"language":{"$ref":"#/components/schemas/Language"},"pending_action":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PendingActionView"}]},"actions":{"type":"array","items":{"$ref":"#/components/schemas/ReplyAction"}}}},"ContributionDto":{"type":"object","required":["id","group_id","member_id","cycle_number","amount","currency","status","reference","msisdn"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"},"cycle_number":{"type":"integer","format":"int32"},"amount":{"type":"string"},"currency":{"type":"string"},"status":{"$ref":"#/components/schemas/ContributionStatus"},"reference":{"type":"string"},"msisdn":{"type":"string"},"due_id":{"type":["string","null"],"format":"uuid"}}},"ContributionStatus":{"type":"string","enum":["pending","completed","failed"]},"ConvChannel":{"type":"string","enum":["portal","whatsapp","telegram","voice"]},"CreateDueRequest":{"type":"object","required":["title","kind","frequency","amount_per_member"],"properties":{"title":{"type":"string"},"kind":{"$ref":"#/components/schemas/DueKind"},"frequency":{"$ref":"#/components/schemas/DueFrequency"},"amount_per_member":{"type":"string","example":"50.00"}}},"CreateGroupRequest":{"type":"object","required":["name","kind","frequency","moolre_account_number"],"properties":{"name":{"type":"string"},"kind":{"$ref":"#/components/schemas/GroupKind"},"description":{"type":["string","null"]},"contribution_amount":{"type":["string","null"],"example":"50.00"},"min_amount":{"type":["string","null"],"example":"10.00"},"goal_amount":{"type":["string","null"],"example":"10000.00"},"frequency":{"$ref":"#/components/schemas/Frequency"},"moolre_account_number":{"type":"string"},"rotation_enabled":{"type":"boolean"},"require_full_funding":{"type":"boolean"},"visibility":{"$ref":"#/components/schemas/GroupVisibility"},"collection_start_date":{"type":["string","null"],"format":"date"},"collection_end_date":{"type":["string","null"],"format":"date"},"penalize_missed_deadlines":{"type":"boolean"}}},"CreatePayoutRequest":{"type":"object","required":["group_id"],"properties":{"group_id":{"type":"string","format":"uuid"}}},"CreateUserRequest":{"type":"object","required":["password","full_name","role"],"properties":{"email":{"type":["string","null"],"description":"Optional secondary identifier."},"msisdn":{"type":["string","null"],"description":"Required Ghana phone number (E.164 `+233…`)."},"password":{"type":"string"},"full_name":{"type":"string"},"role":{"$ref":"#/components/schemas/AdminRole"}}},"CycleStatusDto":{"type":"object","required":["group_id","cycle_number","amount_saved","collected","expected","remaining","members_total","members_paid","members"],"properties":{"group_id":{"type":"string","format":"uuid"},"cycle_number":{"type":"integer","format":"int32"},"contribution_amount":{"type":["string","null"]},"goal_amount":{"type":["string","null"]},"amount_saved":{"type":"string"},"collected":{"type":"string"},"expected":{"type":"string"},"remaining":{"type":"string"},"members_total":{"type":"integer","format":"int64"},"members_paid":{"type":"integer","format":"int64"},"members":{"type":"array","items":{"$ref":"#/components/schemas/MemberCycleStatus"}}}},"DiscoverListDto":{"type":"object","required":["groups"],"properties":{"groups":{"type":"array","items":{"$ref":"#/components/schemas/GroupPreviewDto"}}}},"DraftReminderRequest":{"type":"object","required":["group_id","member_id"],"properties":{"group_id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"}}},"DueDto":{"type":"object","required":["id","group_id","title","kind","frequency","amount_per_member","status","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"kind":{"$ref":"#/components/schemas/DueKind"},"frequency":{"$ref":"#/components/schemas/DueFrequency"},"amount_per_member":{"type":"string"},"status":{"$ref":"#/components/schemas/DueStatus"},"created_at":{"type":"string","format":"date-time"},"closed_at":{"type":["string","null"],"format":"date-time"}}},"DueFrequency":{"type":"string","enum":["once","daily","weekly","monthly"]},"DueKind":{"type":"string","enum":["fixed_due","recurring_susu"]},"DueMemberStatus":{"type":"object","required":["member_id","full_name","msisdn","state","paid","outstanding"],"properties":{"member_id":{"type":"string","format":"uuid"},"full_name":{"type":"string"},"msisdn":{"type":"string"},"state":{"$ref":"#/components/schemas/MemberCycleState"},"paid":{"type":"string"},"outstanding":{"type":"string"}}},"DueStatus":{"type":"string","enum":["active","closed"]},"DueStatusView":{"type":"object","required":["due_id","title","amount_per_member","collected","expected","members_total","members_paid","members"],"properties":{"due_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"amount_per_member":{"type":"string"},"collected":{"type":"string"},"expected":{"type":"string"},"members_total":{"type":"integer","format":"int64"},"members_paid":{"type":"integer","format":"int64"},"members":{"type":"array","items":{"$ref":"#/components/schemas/DueMemberStatus"}}}},"EditDraftRequest":{"type":"object","required":["body"],"properties":{"body":{"type":"string"}}},"ErrorCode":{"type":"string","enum":["validation_failed","missing_required_field","invalid_currency","invalid_phone_number","invalid_credentials","invalid_token","expired_token","account_locked","account_not_verified","insufficient_permissions","webhook_verify_failed","resource_not_found","resource_conflict","idempotency_conflict","payload_too_large","rate_limit_exceeded","provider_error","database_error","service_unavailable","not_implemented","internal_error"]},"ErrorResponse":{"type":"object","description":"Wire shape of every API error response.","required":["error"],"properties":{"error":{"$ref":"#/components/schemas/ErrorResponseBody"}}},"ErrorResponseBody":{"type":"object","required":["code","message"],"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","example":"request failed validation"},"field":{"type":["string","null"]},"details":{"type":["string","null"]}}},"ExpenseDto":{"type":"object","required":["id","group_id","title","category","amount","spent_on","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"category":{"type":"string"},"payee":{"type":["string","null"]},"amount":{"type":"string"},"spent_on":{"type":"string","format":"date"},"note":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}}},"Frequency":{"type":"string","enum":["daily","weekly","monthly"]},"GroupDto":{"type":"object","required":["id","org_id","name","kind","currency","frequency","rotation_enabled","require_full_funding","status","visibility","moolre_account_number","invite_code","penalize_missed_deadlines","current_cycle","next_position","role"],"properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"kind":{"$ref":"#/components/schemas/GroupKind"},"contribution_amount":{"type":["string","null"]},"min_amount":{"type":["string","null"]},"goal_amount":{"type":["string","null"]},"currency":{"type":"string"},"frequency":{"$ref":"#/components/schemas/Frequency"},"rotation_enabled":{"type":"boolean"},"require_full_funding":{"type":"boolean"},"status":{"type":"string"},"visibility":{"$ref":"#/components/schemas/GroupVisibility"},"moolre_account_number":{"type":"string"},"invite_code":{"type":"string"},"description":{"type":["string","null"]},"collection_start_date":{"type":["string","null"],"format":"date"},"collection_end_date":{"type":["string","null"],"format":"date"},"penalize_missed_deadlines":{"type":"boolean"},"current_cycle":{"type":"integer","format":"int32"},"next_position":{"type":"integer","format":"int32"},"collection_start_at":{"type":["string","null"],"format":"date-time"},"role":{"type":"string"}}},"GroupKind":{"type":"string","enum":["susu","church","pta","trader","welfare","ngo","target"]},"GroupPreviewDto":{"type":"object","required":["group_id","name","kind","currency","frequency","member_count","run_by"],"properties":{"group_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"kind":{"$ref":"#/components/schemas/GroupKind"},"contribution_amount":{"type":["string","null"]},"goal_amount":{"type":["string","null"]},"currency":{"type":"string"},"frequency":{"$ref":"#/components/schemas/Frequency"},"member_count":{"type":"integer","format":"int64"},"run_by":{"type":"string"},"group_score":{"type":["integer","null"],"format":"int32"},"membership_status":{"type":["string","null"]},"match_reason":{"type":["string","null"]}}},"GroupVisibility":{"type":"string","enum":["public","private"]},"InitiateContributionRequest":{"type":"object","required":["group_id","member_id"],"properties":{"group_id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"},"amount":{"type":["string","null"],"example":"10.00"},"due_id":{"type":["string","null"],"format":"uuid"}}},"InviteAdminRequest":{"type":"object","required":["full_name","role"],"properties":{"email":{"type":["string","null"],"description":"Optional secondary identifier. The verification code is sent to the phone,\nor to this email when no SMS channel is available."},"msisdn":{"type":["string","null"],"description":"Required Ghana phone number (E.164 `+233…`)."},"full_name":{"type":"string"},"role":{"$ref":"#/components/schemas/AdminRole"},"channel":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/VerifyChannel","description":"Verification channel; defaults to SMS (the phone)."}]}}},"JoinGroupRequest":{"type":"object","required":["msisdn","full_name"],"properties":{"msisdn":{"type":"string"},"full_name":{"type":"string"},"email":{"type":["string","null"]}}},"JoinResultDto":{"type":"object","required":["member_id","group_id","group_name","status"],"properties":{"member_id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"group_name":{"type":"string"},"status":{"type":"string"}}},"Language":{"type":"string","enum":["en","fr","tw","ee","ga","ha"]},"LinkTokenDto":{"type":"object","required":["code","expires_in_seconds"],"properties":{"code":{"type":"string"},"deep_link":{"type":"string"},"expires_in_seconds":{"type":"integer","format":"int64"}}},"LoginRequest":{"type":"object","required":["password"],"properties":{"email":{"type":["string","null"],"description":"Email or `msisdn` is required; provide the one used at signup."},"msisdn":{"type":["string","null"],"description":"Ghana phone number (E.164 `+233…`); an alternative to `email`."},"password":{"type":"string"}}},"MatchGroupRequest":{"type":"object","required":["query"],"properties":{"query":{"type":"string"}}},"MeResponse":{"type":"object","required":["msisdn","memberships"],"properties":{"msisdn":{"type":"string"},"memberships":{"type":"array","items":{"$ref":"#/components/schemas/MembershipDto"}}}},"MemberCycleState":{"type":"string","enum":["paid","part_paid","pending","owing"]},"MemberCycleStatus":{"type":"object","required":["member_id","full_name","msisdn","state","paid","outstanding"],"properties":{"member_id":{"type":"string","format":"uuid"},"full_name":{"type":"string"},"msisdn":{"type":"string"},"state":{"$ref":"#/components/schemas/MemberCycleState"},"paid":{"type":"string"},"outstanding":{"type":"string"}}},"MemberDto":{"type":"object","required":["id","group_id","msisdn","full_name","network","status"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"msisdn":{"type":"string"},"full_name":{"type":"string"},"network":{"$ref":"#/components/schemas/Network"},"rotation_position":{"type":["integer","null"],"format":"int32"},"status":{"type":"string"}}},"MemberRefreshRequest":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string"}}},"MemberTokenResponse":{"type":"object","required":["access_token","refresh_token","token_type","access_expires_in_seconds","refresh_expires_in_seconds","msisdn"],"properties":{"access_token":{"type":"string"},"refresh_token":{"type":"string"},"token_type":{"type":"string"},"access_expires_in_seconds":{"type":"integer","format":"int64","minimum":0},"refresh_expires_in_seconds":{"type":"integer","format":"int64","minimum":0},"msisdn":{"type":"string"}}},"MembershipDto":{"type":"object","required":["member_id","group_id","group_name","frequency","currency","cycle_number","state","paid","outstanding"],"properties":{"member_id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"group_name":{"type":"string"},"contribution_amount":{"type":["string","null"]},"frequency":{"type":"string"},"currency":{"type":"string"},"rotation_position":{"type":["integer","null"],"format":"int32"},"cycle_number":{"type":"integer","format":"int32"},"state":{"$ref":"#/components/schemas/MemberCycleState"},"paid":{"type":"string"},"outstanding":{"type":"string"},"score":{"type":["integer","null"],"format":"int32"}}},"Network":{"type":"string","enum":["mtn","telecel","at"]},"NotificationDto":{"type":"object","required":["id","kind","payload","read","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"kind":{"$ref":"#/components/schemas/NotificationKind"},"group_id":{"type":["string","null"],"format":"uuid"},"payload":{"type":"object"},"read":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}},"NotificationKind":{"type":"string","enum":["contribution_settled","payout_initiated","payout_settled","member_left","member_approved","member_rejected"]},"PaginatedResponse_ContributionDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","group_id","member_id","cycle_number","amount","currency","status","reference","msisdn"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"},"cycle_number":{"type":"integer","format":"int32"},"amount":{"type":"string"},"currency":{"type":"string"},"status":{"$ref":"#/components/schemas/ContributionStatus"},"reference":{"type":"string"},"msisdn":{"type":"string"},"due_id":{"type":["string","null"],"format":"uuid"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_DueDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","group_id","title","kind","frequency","amount_per_member","status","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"kind":{"$ref":"#/components/schemas/DueKind"},"frequency":{"$ref":"#/components/schemas/DueFrequency"},"amount_per_member":{"type":"string"},"status":{"$ref":"#/components/schemas/DueStatus"},"created_at":{"type":"string","format":"date-time"},"closed_at":{"type":["string","null"],"format":"date-time"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_ExpenseDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","group_id","title","category","amount","spent_on","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"category":{"type":"string"},"payee":{"type":["string","null"]},"amount":{"type":"string"},"spent_on":{"type":"string","format":"date"},"note":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_GroupDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","org_id","name","kind","currency","frequency","rotation_enabled","require_full_funding","status","visibility","moolre_account_number","invite_code","penalize_missed_deadlines","current_cycle","next_position","role"],"properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"kind":{"$ref":"#/components/schemas/GroupKind"},"contribution_amount":{"type":["string","null"]},"min_amount":{"type":["string","null"]},"goal_amount":{"type":["string","null"]},"currency":{"type":"string"},"frequency":{"$ref":"#/components/schemas/Frequency"},"rotation_enabled":{"type":"boolean"},"require_full_funding":{"type":"boolean"},"status":{"type":"string"},"visibility":{"$ref":"#/components/schemas/GroupVisibility"},"moolre_account_number":{"type":"string"},"invite_code":{"type":"string"},"description":{"type":["string","null"]},"collection_start_date":{"type":["string","null"],"format":"date"},"collection_end_date":{"type":["string","null"],"format":"date"},"penalize_missed_deadlines":{"type":"boolean"},"current_cycle":{"type":"integer","format":"int32"},"next_position":{"type":"integer","format":"int32"},"collection_start_at":{"type":["string","null"],"format":"date-time"},"role":{"type":"string"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_GroupPreviewDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["group_id","name","kind","currency","frequency","member_count","run_by"],"properties":{"group_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"kind":{"$ref":"#/components/schemas/GroupKind"},"contribution_amount":{"type":["string","null"]},"goal_amount":{"type":["string","null"]},"currency":{"type":"string"},"frequency":{"$ref":"#/components/schemas/Frequency"},"member_count":{"type":"integer","format":"int64"},"run_by":{"type":"string"},"group_score":{"type":["integer","null"],"format":"int32"},"membership_status":{"type":["string","null"]},"match_reason":{"type":["string","null"]}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_MemberDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","group_id","msisdn","full_name","network","status"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"msisdn":{"type":"string"},"full_name":{"type":"string"},"network":{"$ref":"#/components/schemas/Network"},"rotation_position":{"type":["integer","null"],"format":"int32"},"status":{"type":"string"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_NotificationDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","kind","payload","read","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"kind":{"$ref":"#/components/schemas/NotificationKind"},"group_id":{"type":["string","null"],"format":"uuid"},"payload":{"type":"object"},"read":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_PayoutDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","group_id","cycle_number","recipient_id","amount","status","reference"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"cycle_number":{"type":"integer","format":"int32"},"recipient_id":{"type":"string","format":"uuid"},"amount":{"type":"string"},"status":{"$ref":"#/components/schemas/PayoutStatus"},"reference":{"type":"string"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_ReminderDraftDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","group_id","member_id","cycle_number","msisdn","body","status","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"},"cycle_number":{"type":"integer","format":"int32"},"msisdn":{"type":"string"},"body":{"type":"string"},"status":{"$ref":"#/components/schemas/ReminderDraftStatus"},"created_at":{"type":"string","format":"date-time"},"sent_at":{"type":["string","null"],"format":"date-time"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_SmsDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","msisdn","body","kind","status","attempts","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"msisdn":{"type":"string"},"body":{"type":"string"},"kind":{"type":"string"},"status":{"$ref":"#/components/schemas/SmsStatus"},"attempts":{"type":"integer","format":"int32"},"created_at":{"type":"string","format":"date-time"},"sent_at":{"type":["string","null"],"format":"date-time"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_ThreadMessageDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["role","content","channel","created_at"],"properties":{"role":{"type":"string"},"content":{"type":"string"},"channel":{"$ref":"#/components/schemas/ConvChannel"},"created_at":{"type":"string","format":"date-time"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_ThreadSummaryDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["thread_id","preview","language","updated_at"],"properties":{"thread_id":{"type":"string","format":"uuid"},"preview":{"type":"string"},"language":{"$ref":"#/components/schemas/Language"},"updated_at":{"type":"string","format":"date-time"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_TrustScoreDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["member_id","full_name","missed_payments","early_payments"],"properties":{"member_id":{"type":"string","format":"uuid"},"full_name":{"type":"string"},"score":{"type":["integer","null"],"format":"int32"},"missed_payments":{"type":"integer","format":"int32"},"early_payments":{"type":"integer","format":"int32"},"note":{"type":["string","null"]}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PaginatedResponse_UserDto":{"type":"object","description":"FastAPI-style pagination envelope wrapping any page of list results.","required":["items","total","page","size","total_pages"],"properties":{"items":{"type":"array","items":{"type":"object","required":["id","org_id","full_name","role"],"properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]},"msisdn":{"type":["string","null"]},"full_name":{"type":"string"},"role":{"$ref":"#/components/schemas/AdminRole"}}}},"total":{"type":"integer","format":"int64"},"page":{"type":"integer","format":"int64"},"size":{"type":"integer","format":"int64"},"total_pages":{"type":"integer","format":"int64"}}},"PayoutDto":{"type":"object","required":["id","group_id","cycle_number","recipient_id","amount","status","reference"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"cycle_number":{"type":"integer","format":"int32"},"recipient_id":{"type":"string","format":"uuid"},"amount":{"type":"string"},"status":{"$ref":"#/components/schemas/PayoutStatus"},"reference":{"type":"string"}}},"PayoutStatus":{"type":"string","enum":["pending","completed","failed"]},"PendingActionView":{"type":"object","required":["kind","summary","amount_display","expires_at"],"properties":{"kind":{"type":"string"},"group_id":{"type":["string","null"],"format":"uuid"},"summary":{"type":"string"},"amount_display":{"type":"string"},"requires_step_up":{"type":"boolean"},"expires_at":{"type":"string","format":"date-time"}}},"RecordExpenseRequest":{"type":"object","required":["title","category","amount","spent_on"],"properties":{"title":{"type":"string"},"category":{"type":"string"},"payee":{"type":["string","null"]},"amount":{"type":"string","example":"21.00"},"spent_on":{"type":"string","format":"date"},"note":{"type":["string","null"]}}},"RefreshRequest":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string"}}},"ReminderDraftDto":{"type":"object","required":["id","group_id","member_id","cycle_number","msisdn","body","status","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"},"cycle_number":{"type":"integer","format":"int32"},"msisdn":{"type":"string"},"body":{"type":"string"},"status":{"$ref":"#/components/schemas/ReminderDraftStatus"},"created_at":{"type":"string","format":"date-time"},"sent_at":{"type":["string","null"],"format":"date-time"}}},"ReminderDraftStatus":{"type":"string","enum":["pending","approved","sent","discarded"]},"RemindersQueuedDto":{"type":"object","required":["queued"],"properties":{"queued":{"type":"integer","format":"int64"}}},"ReplyAction":{"type":"object","required":["id","label"],"properties":{"id":{"type":"string"},"label":{"type":"string"}}},"RequestOtpRequest":{"type":"object","required":["msisdn"],"properties":{"msisdn":{"type":"string"}}},"ResendVerificationRequest":{"type":"object","properties":{"email":{"type":["string","null"]},"msisdn":{"type":["string","null"]}}},"RotationEntry":{"type":"object","required":["position","member_id","full_name","is_next"],"properties":{"position":{"type":"integer","format":"int32"},"member_id":{"type":"string","format":"uuid"},"full_name":{"type":"string"},"is_next":{"type":"boolean"}}},"RotationView":{"type":"object","required":["group_id","current_cycle","next_position","order"],"properties":{"group_id":{"type":"string","format":"uuid"},"current_cycle":{"type":"integer","format":"int32"},"next_position":{"type":"integer","format":"int32"},"order":{"type":"array","items":{"$ref":"#/components/schemas/RotationEntry"}}}},"SelfContributionRequest":{"type":"object","required":["group_id"],"properties":{"group_id":{"type":"string","format":"uuid"},"amount":{"type":["string","null"],"example":"10.00"},"due_id":{"type":["string","null"],"format":"uuid"}}},"SendRemindersRequest":{"type":"object","properties":{"tone":{"$ref":"#/components/schemas/Tone"}}},"SendTestRequest":{"type":"object","required":["msisdn","body"],"properties":{"msisdn":{"type":"string"},"body":{"type":"string"}}},"SignupRequest":{"type":"object","required":["org_name","password","full_name"],"properties":{"org_name":{"type":"string"},"email":{"type":["string","null"],"description":"Optional secondary identifier."},"msisdn":{"type":["string","null"],"description":"Required Ghana phone number (E.164 `+233…`)."},"password":{"type":"string"},"full_name":{"type":"string"},"channel":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/VerifyChannel","description":"Verification channel; defaults to SMS (the phone)."}]}}},"SmsDto":{"type":"object","required":["id","msisdn","body","kind","status","attempts","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"msisdn":{"type":"string"},"body":{"type":"string"},"kind":{"type":"string"},"status":{"$ref":"#/components/schemas/SmsStatus"},"attempts":{"type":"integer","format":"int32"},"created_at":{"type":"string","format":"date-time"},"sent_at":{"type":["string","null"],"format":"date-time"}}},"SmsStatus":{"type":"string","enum":["queued","sent","failed"]},"ThreadMessageDto":{"type":"object","required":["role","content","channel","created_at"],"properties":{"role":{"type":"string"},"content":{"type":"string"},"channel":{"$ref":"#/components/schemas/ConvChannel"},"created_at":{"type":"string","format":"date-time"}}},"ThreadSummaryDto":{"type":"object","required":["thread_id","preview","language","updated_at"],"properties":{"thread_id":{"type":"string","format":"uuid"},"preview":{"type":"string"},"language":{"$ref":"#/components/schemas/Language"},"updated_at":{"type":"string","format":"date-time"}}},"TokenResponse":{"type":"object","required":["access_token","refresh_token","token_type","access_expires_in_seconds","refresh_expires_in_seconds","admin"],"properties":{"access_token":{"type":"string"},"refresh_token":{"type":"string"},"token_type":{"type":"string"},"access_expires_in_seconds":{"type":"integer","format":"int64","minimum":0},"refresh_expires_in_seconds":{"type":"integer","format":"int64","minimum":0},"admin":{"$ref":"#/components/schemas/AdminDto"}}},"Tone":{"type":"string","enum":["polite","warm","firm","urgent"]},"TransparencyDto":{"type":"object","required":["group_id","group_name","currency","collected","paid_out","balance","members_total","members_contributed","spend_by_category"],"properties":{"group_id":{"type":"string","format":"uuid"},"group_name":{"type":"string"},"currency":{"type":"string"},"collected":{"type":"string"},"paid_out":{"type":"string"},"balance":{"type":"string"},"members_total":{"type":"integer","format":"int64"},"members_contributed":{"type":"integer","format":"int64"},"spend_by_category":{"type":"array","items":{"$ref":"#/components/schemas/CategorySpend"}}}},"TrustScoreDto":{"type":"object","required":["member_id","full_name","missed_payments","early_payments"],"properties":{"member_id":{"type":"string","format":"uuid"},"full_name":{"type":"string"},"score":{"type":["integer","null"],"format":"int32"},"missed_payments":{"type":"integer","format":"int32"},"early_payments":{"type":"integer","format":"int32"},"note":{"type":["string","null"]}}},"UnreadCountDto":{"type":"object","required":["unread"],"properties":{"unread":{"type":"integer","format":"int64"}}},"UpdateGroupRequest":{"type":"object","properties":{"name":{"type":["string","null"]},"description":{"type":["string","null"]},"contribution_amount":{"type":["string","null"],"example":"50.00"},"min_amount":{"type":["string","null"],"example":"10.00"},"goal_amount":{"type":["string","null"],"example":"10000.00"},"visibility":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/GroupVisibility"}]},"collection_start_date":{"type":["string","null"],"format":"date"},"collection_end_date":{"type":["string","null"],"format":"date"},"penalize_missed_deadlines":{"type":["boolean","null"]},"collection_start_at":{"type":["string","null"],"format":"date-time"}}},"UpdateMemberRequest":{"type":"object","properties":{"full_name":{"type":["string","null"]},"status":{"type":["string","null"]},"network":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Network"}]}}},"UpdateRoleRequest":{"type":"object","required":["role"],"properties":{"role":{"$ref":"#/components/schemas/AdminRole"}}},"UserDto":{"type":"object","required":["id","org_id","full_name","role"],"properties":{"id":{"type":"string","format":"uuid"},"org_id":{"type":"string","format":"uuid"},"email":{"type":["string","null"]},"msisdn":{"type":["string","null"]},"full_name":{"type":"string"},"role":{"$ref":"#/components/schemas/AdminRole"}}},"UssdRequest":{"type":"object","required":["sessionid","msisdn"],"properties":{"sessionid":{"type":"string"},"msisdn":{"type":"string"},"text":{"type":"string"}}},"VerificationRequiredResponse":{"type":"object","required":["channel","expires_in_seconds","message"],"properties":{"channel":{"type":"string","description":"Channel the code was dispatched over: `sms` or `email`."},"expires_in_seconds":{"type":"integer","format":"int64"},"message":{"type":"string"}}},"VerifyChannel":{"type":"string","description":"Where a verification code is delivered.","enum":["sms","email"]},"VerifyOtpRequest":{"type":"object","required":["msisdn","code"],"properties":{"msisdn":{"type":"string"},"code":{"type":"string"}}},"VerifySignupRequest":{"type":"object","required":["code"],"properties":{"email":{"type":["string","null"],"description":"The email or `msisdn` used at signup; provide the one a code was sent to."},"msisdn":{"type":["string","null"]},"code":{"type":"string"}}},"VoiceReplyDto":{"type":"object","required":["reply","language"],"properties":{"reply":{"type":"string"},"transcript":{"type":["string","null"]},"thread_id":{"type":["string","null"],"format":"uuid"},"language":{"$ref":"#/components/schemas/Language"},"pending_action":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PendingActionView"}]},"actions":{"type":"array","items":{"$ref":"#/components/schemas/ReplyAction"}},"reply_audio_url":{"type":["string","null"]}}}},"securitySchemes":{"bearer_auth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"security":[{"bearer_auth":[]}]}