Ownware
Home›Membora›Manual
Membora · Manual

Membora Manual, as shipped in the download

Membora — User Manual

A membership register for a club or association: members, subscriptions, meetings, attendance, motions and money. Version 1.0.3 [src: app/controllers/api.php:16].

About this manual

Every statement here was written by reading Membora's own source, and each one carries the file it came from in a bracketed src marker. Paths are relative to the folder this docs directory sits in. If a sentence disagrees with the application, the application is right and this manual has a bug.

Nothing is described that the code does not do. Where something is deliberately limited, or currently wrong, that is said plainly rather than left out.

⭐ The voting guard, and where it is applied

Nearly every club constitution says some version of "a member whose subscription is in arrears may not vote". The source names that sentence as the reason the subscription record and the vote record belong in one product: kept apart, the secretary is reconciling a spreadsheet against a minute book at the moment a decision is being taken [src: app/src/Membership.php:172].

The design decision that follows is the one to understand:

The rule is applied at the ballot, never at the door. A member in arrears is still recorded as present when they were present — attendance is a record of fact, and quorum is counted from it. Only the right to vote is withheld [src: app/src/Membership.php:177].

Executed, with today at 2026-09-05 — the guard and the exact reason it gives [src: app/src/Membership.php:184]:

no subscription on file    no    "No current subscription on file, so no vote is recorded.
                                  Members in arrears may attend and be counted toward quorum."
active, ends 2026-12-31    YES
frozen                     no    "Subscription is suspended, so no vote is recorded."
cancelled                  no    "Membership has been resigned, so no vote is recorded."
active, ended 2026-08-01   no    "Subscription lapsed on 2026-08-01, so no vote is recorded."

Note the last line: the reason names the date it lapsed, which is what a member at a meeting will ask for.

⭐ Status is what is true today, not what was typed

An active membership whose end date has passed is really expired, and the register says so without anybody having to run anything [src: app/src/Membership.php:154]. Frozen and cancelled are manual holds and are never auto-flipped — a suspension does not quietly become an expiry.

Executed:

stored active     ends 2026-09-06  ->  active
stored active     ends 2026-09-05  ->  active      ← the end date itself is still inside the term
stored active     ends 2026-09-04  ->  expired
stored frozen     ended 2026-01-01 ->  frozen      (never auto-flipped)
stored cancelled  ended 2026-01-01 ->  cancelled
no membership                      ->  none

Terms, and the month-end case

Four billing periods [src: app/src/Membership.php:27]. Life and honorary memberships are open-ended — they have no end date at all rather than a distant one [src: app/src/Membership.php:240]. Executed from an awkward start date:

annual     start 2026-01-31  ->  ends 2027-01-31
monthly    start 2026-01-31  ->  ends 2026-02-28      ← clamped, not rolled into March
life       start 2026-01-31  ->  open-ended
honorary   start 2026-01-31  ->  open-ended

The meeting record: minutes, the roll, the tallies

Each meeting's roll page carries a Meeting record card. Minutes can be typed or corrected there at any time after the meeting [src: app/controllers/v3.php:733], and each change is written to the audit trail as a change, without the text. The same card downloads the record as PDF and CSV [src: app/controllers/v3.php:747]: the meeting, the quorum on the day (present against required, and whether it was quorate), the roll with each member's recorded state, each motion with its mover, seconder, votes for, against and abstaining, and outcome, and the minutes. The quorum and the tallies come from the same functions the screens use, so the record cannot disagree with them.

The renewals list only contains people you should actually chase

"About to lapse" means an active, dated term whose end date falls between today and the window you set [src: app/src/Membership.php:266]. Already-lapsed, suspended, resigned and open-ended subscriptions never appear — the source names the error it is avoiding: chasing a life member for a renewal [src: app/src/Membership.php:264].

Executed with a thirty-day window:

active   ends 2026-09-20  ->  on the list
active   ends 2026-10-20  ->  not listed (outside the window)
active   ended 2026-09-04 ->  not listed (already lapsed — chase it as arrears, not a renewal)
frozen   ends 2026-09-20  ->  not listed
active   open-ended       ->  not listed

That third line is worth knowing: a membership that has already lapsed falls off the renewals list entirely, because it is no longer a renewal question.

Arrears

A payment is overdue when it is unpaid and its due date is strictly before today — a payment due today is not yet overdue [src: app/src/Membership.php:277]. Executed:

due 2026-09-04  unpaid  ->  overdue
due 2026-09-05  unpaid  ->  not overdue      ← due today
due 2026-09-06  unpaid  ->  not overdue
due 2026-09-01  paid    ->  not overdue

arrears total of the unpaid rows above: £74.99

Money is read the way people actually type it

Amounts are integer cents throughout [src: app/src/Membership.php:16], and the parser accepts both conventions [src: app/src/Membership.php:37]. Executed:

'$49.99'      ->  4,999 cents        49      ->  4,900 cents
'1.234,56 €'  ->  123,456 cents      49.9    ->  4,990 cents
'1,234.56'    ->  123,456 cents      '-12,50' -> −1,250 cents
'free'        ->  null — no number, and it says so rather than guessing zero

Motions cannot be decided in an inquorate meeting

Carrying or rejecting a motion is refused when the meeting was not quorate, and the refusal shows the arithmetic — how many were present against how many were required [src: app/controllers/v3.php:1020].

The event that follows is fired after the write, and the source explains why: so a subscriber can never learn of an outcome that was rolled back [src: app/controllers/v3.php:1026].

Users and roles

Roles are held per permission [src: app/controllers/app.php:25], with the audit page requiring audit.view [src: app/controllers/v3.php:16].

⚠️ A viewer can read the audit trail

The viewer role holds the wildcard *.view [src: app/controllers/app.php:25], which matches audit.view. If your read-only accounts are meant not to see who recorded which vote or which payment, that is not the behaviour you have. This has been raised against the product.

Backups are gated on settings.edit [src: app/controllers/api.php:293], which no role below admin holds [src: app/controllers/api.php:299].

Webhooks

Four events are offered for subscription [src: app/src/Webhook.php:21], and each is sent:

eventsent when
meeting.attendanceattendance is recorded — from the roll call, the meeting page, the REST API or the agent, which all go through one function [src: app/controllers/app.php:133]
attendance.refusedattendance could not be recorded (a cancelled or full meeting), with the reason
payment.recordeda payment lands: added in the browser or through the REST API, or a due payment marked paid
motion.decideda motion is decided

An endpoint saved before 1.0.3 under the old name meeting.attended receives meeting.attendance.

The API and agent access

Eight tools [src: app/controllers/mcp.php:61]: six read — the member list, one member, the meeting list, one meeting, the lapsing report and the subscription report — and two write, recording attendance [src: app/controllers/mcp.php:177] and recording a vote [src: app/controllers/mcp.php:208].

Recording attendance through an agent requires the same meeting.attend permission the screens do [src: app/controllers/mcp.php:191], so an automation cannot do what its key's role could not do by hand.

What Membora does not do

It does not stop a member in arrears at the door [src: app/src/Membership.php:177] — attendance is a record of fact and quorum counts it.

It does not auto-flip a suspension into an expiry [src: app/src/Membership.php:154].

It does not put a life member on the renewals list [src: app/src/Membership.php:264].

It does not treat a payment due today as overdue [src: app/src/Membership.php:277].

It does not guess zero when it cannot read an amount [src: app/src/Membership.php:37].

It does not let an inquorate meeting decide a motion [src: app/controllers/v3.php:1020].

← Back to Membora · Quickstart · API · Test run

Affiliate program
Recommend tools people own — earn 35% on every sale. 90-day tracking, instant delivery, payouts by Lemon Squeezy.
Become an affiliate →