Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.digiflecttech.dev/llms.txt

Use this file to discover all available pages before exploring further.

A cycle is a single contribution round in your savings group. During a cycle every active member is expected to pay a fixed contribution_amount by a set contribution_due_date, and each member receives a payout exactly once — in an order determined by their credit score. Only one cycle can be active at a time. When all payouts have been disbursed the admin ends the cycle, credit scores are updated, and the group is ready to start the next round.

Cycle Lifecycle

[Admin starts cycle] → ACTIVE → [All contributions paid, payouts disbursed] → [Admin ends cycle] → COMPLETED
The status field on a cycle object will be one of:
StatusMeaning
ACTIVEThe cycle is running; contributions are being collected and payouts are being processed
COMPLETEDThe cycle has ended; all members received their payouts
CANCELLEDThe cycle was cancelled before completion (rare administrative action)

Key Cycle Fields

FieldTypeDescription
idstringUnique cycle identifier
cycle_numberintegerAuto-incrementing sequence number (1, 2, 3…)
contribution_amountfloatFixed amount every member must pay this cycle
contribution_due_datedatetimeDeadline for all member contributions
start_datedatetimeWhen the cycle officially began
end_datedatetime | nullWhen the cycle was closed; null while active
statusstringACTIVE or COMPLETED

API Endpoints

Start a new cycle

POST /api/cycles/startAdmin only Creates a new cycle and immediately generates the payout queue based on current credit scores. If contribution_amount is omitted, the value from system configuration (SystemConfig.contribution_amount) is used.
{
  "contribution_amount": 50000,
  "contribution_due_date": "2025-07-31T23:59:59",
  "start_date": "2025-07-01T00:00:00"
}
Starting a new cycle when one is already ACTIVE returns a 400 Bad Request. You must end the current cycle before starting another.

Get the current active cycle

GET /api/cycles/current — Any authenticated user Returns the single active cycle, or 404 if none exists.

Get cycle statistics

GET /api/cycles/{cycle_id}/statistics — Any authenticated user Returns a real-time snapshot of how the cycle is progressing.
{
  "cycle_number": 4,
  "status": "ACTIVE",
  "contribution_amount": 50000.0,
  "contribution_due_date": "2025-07-31T23:59:59",
  "total_members": 12,
  "members_paid_contribution": 9,
  "members_received_payout": 3,
  "total_contributions_collected": 450000.0,
  "is_complete": false
}

Get the payout queue

GET /api/cycles/{cycle_id}/queue — Any authenticated user Returns the full ordered list of members and their payout status for the cycle. The queue is generated once at cycle creation using each member’s credit score at that moment (credit_score_at_generation).
[
  {
    "id": "qe-001",
    "member_id": "usr-abc",
    "member_name": "Grace Akello",
    "position": 1,
    "credit_score_at_generation": 720.0,
    "has_received_payout": true,
    "payout_date": "2025-07-10T00:00:00",
    "actual_payout_date": "2025-07-10T14:32:00"
  },
  {
    "id": "qe-002",
    "member_id": "usr-def",
    "member_name": "Peter Okello",
    "position": 2,
    "credit_score_at_generation": 655.0,
    "has_received_payout": false,
    "payout_date": null,
    "actual_payout_date": null
  }
]

End a cycle

POST /api/cycles/{cycle_id}/endAdmin only Closes the cycle and records a cycles_completed event on every member’s credit score. The endpoint returns 400 if any queue member has not yet received their payout.
{ "message": "Cycle ended successfully" }

Check contribution completion

GET /api/cycles/{cycle_id}/contributions-complete — Any authenticated user A lightweight check that returns true when every active member has paid the full contribution_amount.
{ "all_contributions_received": true }

The Payout Queue

When a cycle starts, Save App calls get_member_rankings to retrieve all active members sorted by credit score (highest first). It creates a PayoutQueue row for each member, locking in their credit_score_at_generation and position. That order does not change during the cycle — it is a snapshot taken at the moment the cycle was created.
A member must have their contribution paid in full before the group can process any payout in that cycle. The can_process_payout check enforces this: if all_contributions_received is false, no payout will be approved.
Payouts are processed in ascending position order. Each admin must approve a payout before it is executed. Once executed, has_received_payout is set to true for that queue entry and actual_payout_date is recorded.

Frequently Asked Questions

Your contribution is still accepted after the due date, but it is recorded as a late payment, which reduces your credit score. A late payment carries a -10 point penalty by default. Missed payments (never paid) carry a -20 penalty. Your position in the queue for the current cycle is already fixed, but your score will affect your queue position in the next cycle.
No. The queue is a snapshot generated at cycle creation. Subsequent changes to credit scores during the cycle do not affect the current queue. The updated scores will influence the queue generated when the next cycle starts.
New members added while a cycle is already ACTIVE are not automatically added to the existing queue. They will appear in the queue for the next cycle that starts after their account becomes active.
Yes. If you supply contribution_amount in the POST /api/cycles/start body it overrides the system-wide default for that cycle only. If you omit it, the system configuration value (SystemConfig.contribution_amount, default 50,000) is used.
is_complete: true means every member in the payout queue for that cycle has has_received_payout: true. It is a prerequisite for calling POST /api/cycles/{cycle_id}/end — the end endpoint will reject the request if is_complete is still false.