Appearance
Account, voucher and top-up fields
Field by field, for the three things a guest can log in or top up with.
Users and vouchers are the same table: kiwire_users, told apart by ktype (account for a user, voucher for a voucher). Top-up codes are their own table, kiwire_topup_code. Types and defaults below are read from the live tenant schema; the "Form" column says whether the admin form exposes the field or the system fills it.
Users
Menu tour: Account › Users. Permissions: view-users, edit-users (create, edit, reset), delete-users.
kiwire_users where ktype = 'account':
| Field | Type | Meaning | Form |
|---|---|---|---|
username | varchar(120) | Login name, unique per tenant (together with vouchers — a voucher code and a username can't clash). No whitespace or , ; ' " < > \. | Required |
password | varchar(255) | Stored encrypted (reversible, so RADIUS can do CHAP/EAP). Required on create, 6–64 chars; leave blank on edit to keep it. | Required on create |
fullname, email_address, phone_number | varchar(120/120/20) | Contact details. | Optional |
profile_subs | bigint FK → kiwire_profiles.id | The profile the account subscribed to — what it reverts to after an auto-reset. | Required |
profile_curr | bigint FK → kiwire_profiles.id | The profile currently applied — set equal to profile_subs on create/edit, and moved to the profile's advance_profile when a limit is exceeded. The detail view flags when the two differ. | System |
profile_cus | varchar(255), JSON | Bonus credit on top of the profile: {"time": seconds, "quota": bytes} — written by top-up code redemption and added to the profile's limits at login. | System |
integration | varchar(20) | Authentication type: int (internal — the stored password), pms, bc, msad (Active Directory), ldap. Older rows may hold social, payment or internal, which are remapped on edit. | Required |
status | char(20), default active | active, expired or suspended (a legacy suspend is normalised to suspended). Login check step 3 requires active. | Required |
login_type | varchar(50) | How the account was created: blank for staff-created, else the login method — values seen in the wild include one-click, public-signup, social, saml, oauth2-email, whatsapp-otp, payment. The Login Via tab lists every method the account has since used. | System |
subtenant_id | varchar(120) | The subtenant that owns the account, if any; the detail view shows the subtenant's name. | System |
allowed_zone | varchar(120) | Comma-separated list of zone ids (up to 50 on the form, e.g. 3,7). Empty = any zone. Login check step 7 rejects a login from a zone not in the list. | Optional, multi-select |
allowed_mac | varchar(120) | Comma-separated MAC list (AA:BB:CC:DD:EE:FF). Login check step 8 enforces it when the tenant's MAC policy is on. | Optional |
wpa_user | boolean, default 0 | Set the first time the account is issued a WiFi (WPA-Enterprise) certificate through a certificate login method. | System |
date_value | datetime | Start date — a login before it is rejected (check step 5). Set to "now" for portal-created accounts. | — |
date_expiry | datetime | Expiry — a login after it is rejected (check step 5). Entered in the tenant's time zone, stored UTC. | Optional |
date_activate | datetime | First successful login (stamped by check step 12); cleared by Reset. | System |
date_last_login, date_last_logout | datetime | Last session boundaries. | System |
date_password, last_change_pass_at, password_history | datetime / varchar(255) | Password-age bookkeeping carried over from Kiwire 3; shown on the detail view, not enforced by Kiwire 4. | System |
session_time | int, default 0 | Cumulative seconds used — compared to control:Max-All-Session for countdown profiles. | System |
quota_in, quota_out | bigint, default 0 | Cumulative bytes down / up — their sum is compared to the profile's quota. | System |
login_count | int, default 0 | Successful logins since the last reset. | System |
last_reset_at, last_reset_mac_at | datetime | When Reset / Reset MAC last ran. | System |
date_remove | datetime | Removal date for portal-created accounts — set to date_expiry + 30 days when a login method creates the account. | System |
price, total_outstanding | int / double | Amount paid for the plan and outstanding balance (payment login methods). | System |
register_from_nas, register_from_zone, site_name | varchar(255) | Where the account signed up (controller id and zone) and the site label. site_name is on the form. | Mostly system |
campaign_history | varchar(255) | Campaigns the guest has been sent. | System |
extra_fields | longtext, JSON | Key → value pairs collected by the login method's additional fields. | System |
bulk_id | varchar(20) | Batch id — only set on vouchers (below). | — |
remark | text | Free text, max 2000. | Optional |
created_by | char(50) | Admin username that created the row. | System |
Reset (POST /admin/accounts/users/{id}/reset, edit-users) disconnects any live session first, then zeroes quota_in, quota_out, session_time, login_count, clears date_activate, date_last_login, date_last_logout, sets status = active and stamps last_reset_at. Delete is a hard delete (live sessions are closed first).
Detail tabs
Opening a user shows the record plus these tabs, each its own route under /admin/accounts/users/{id}/…:
| Tab | Route | What it reads |
|---|---|---|
| Devices | devices | kiwire_user_device_history — every MAC / device the account has logged in from. |
| Login records | login-records | The monthly kiwire_sessions_* tables — past sessions. |
| Active sessions | active-sessions | kiwire_active_session — sessions open right now. |
| Login via | login-via | kiwire_user_login_via plus the live session table — each login method the account has used, first/last seen, session count. |
| Auto-login | auto-login | kiwire_device_history and kiwire_fingerprint_tokens — the MAC and fingerprint tokens that let the device back in without the portal. |
| Certs | certs | kiwire_cert_lists — WiFi certificates issued to the account. |
| DPI | dpi | Application-level traffic for the account's sessions, fetched from controllers that support it. |
CSV import and export
- Export (
GET /admin/accounts/users/export) writes the current grid filter as CSV with the columnsusername, fullname, email_address, phone_number, profile_subs, status, integration, date_expiry, allowed_zone, allowed_mac, site_name, remark— the profile as its name,allowed_zoneas the stored id list,date_expiryasY-m-din tenant time. - Import (
POST /admin/accounts/users/import, a.csv/.txtup to 2 MB) streams a per-row result back. The header row is required;usernameis the only mandatory column, and rows whose username already exists are skipped. Other recognised columns:password(6+ chars; when the column is absent or empty the account is created without one — "OTP / social login only until one is set"),fullname,email_address,phone_number,status(defaultactive),integration(defaultint),profile_subsorprofile(required — a profile name or id),allowed_zone(comma-separated zone names or ids),allowed_mac,date_expiry(YYYY-MM-DDorYYYY-MM-DD HH:MM, tenant time),site_name,remark. Unknown status, auth type, profile, zone or MAC rejects the row with a note; the rest of the file continues.
Vouchers
Menu tour: Account › Vouchers. Permissions: view-vouchers, create-vouchers, edit-vouchers, reset-vouchers, delete-vouchers.
A voucher is a kiwire_users row with ktype = 'voucher', so every column above applies. What is different:
| Field | On a voucher |
|---|---|
username | The voucher code — upper-case letters, digits, ., -, _, starting with a letter or digit. The password is the code itself. |
profile_curr / profile_subs | Both set to the profile chosen at generation (profile_curr on the form). |
allowed_zone | Comma-separated zone ids as for users; the form accepts a list and drops none. |
integration | Always int. |
status | active on creation. Suspend (the grid's delete action, delete-vouchers) sets suspended and kicks live sessions — vouchers are not hard-deleted one at a time. |
date_expiry | Optional, must be in the future when set. Stamped on the whole batch. |
date_activate | First login — this starts the clock for expiration-type profiles. |
bulk_id | The batch: VC + 8 digits (VC00000001, VC00000002, …), allocated in sequence per tenant. Empty for vouchers created one at a time. |
Generating
POST /admin/accounts/vouchers with creation_mode:
manual— one voucher:username(4–64 chars, the code),profile_curr,allowed_zone,date_expiry,remark. Created immediately. If any group detail (batch_label,visit_date,pax) is given, the code is recorded as a batch of one and gets abulk_id— this is how one shared code for a whole group is counted in the Voucher Groups report.auto— a batch:prefix(required, max 20),quantity(1–100,000), optionalbatch_label,visit_date(YYYY-MM-DD) andpax(1–100,000), plus the same profile / zones / expiry / remark. Kiwire reserves the nextbulk_id, records the batch inkiwire_voucher_generate(bulk_id,quantity,label,visit_date,pax,profile_id,prefix,date_expiry,created_by,remark) and hands the generation to a background job. The job inserts 500 rows at a time; each code isPREFIX+ 8 random upper-case alphanumerics, checked against all usernames on the tenant in one query per chunk so a code can never collide with an account name. The job is unique per batch for 30 minutes, so a retry cannot double-generate. Progress is polled fromGET /admin/accounts/vouchers/generation-status/{bulk_id}.
The Settings card's voucher_engine, voucher_limit and voucher_avoid_ambiguous are not consulted by this job — see Settings, card by card.
Actions
| Action | Route | Permission | Effect |
|---|---|---|---|
| Reset | POST vouchers/{id}/reset | reset-vouchers | Kicks live sessions; zeroes quota_in, quota_out, session_time, login_count; clears date_activate and date_last_login; status = active; stamps last_reset_at. The voucher is as new. |
| Reset MAC | POST vouchers/{id}/reset-mac | reset-vouchers | Clears allowed_mac and stamps last_reset_mac_at, so the code can be used from a different device. |
| Suspend | DELETE vouchers/{id} | delete-vouchers | status = suspended, sessions disconnected with Admin-Suspend. |
| Batch action | POST vouchers/batches/{bulk}/action | edit-vouchers (delete-vouchers for delete_unused) | extend (days 1–3650, pushes date_expiry), suspend_unused, activate, delete_unused (hard-deletes vouchers never logged in), relabel (label, visit_date, pax — all optional; the Group details box on the batch panel). "Unused" = login_count = 0 and no date_activate. |
| Print / export slips | GET vouchers/slip/print, …/slip/export, vouchers/{id}/slip/print, …/slip/export | view-vouchers | Printable slips (using the tenant's voucher_template) or CSV, for one batch (?bulk_id=), one voucher's batch, or everything. |
Vouchers have the same Login records, Devices, Login via and Active sessions tabs as users, at /admin/accounts/vouchers/{id}/….
Top-up codes
Menu tour: Account › Topup Code. Permissions: view-topup-codes, create-topup-codes, edit-topup-codes (edit, reset, redeem), delete-topup-codes.
kiwire_topup_code — one row per code:
| Field | Type | Meaning | Form |
|---|---|---|---|
code | char(20) | The code a guest (or staff) redeems: PREFIX + clength random upper-case alphanumerics, unique per tenant. | Generated |
status | char(20), default active | active (unused) or used. Anything else blocks redemption with "is status". | System |
plan_name | varchar(120) | Label shown to the guest and in the grid. | Required |
price | decimal(11,2) | Face value, informational. | Optional |
quota | bigint, default 0 | Data credit in bytes (the form takes MB, up to 1,073,741,824 MB; stored × 1,048,576). | Optional |
time | int, default 0 | Time credit in seconds (the form takes minutes — converted from hours/days/months — up to 2,628,000 minutes ≈ 5 years; stored × 60). | Optional |
username | char(120) | Who redeemed it; NULL until then. | System |
date_activate | datetime | When it was redeemed. | System |
date_expiry | datetime | Last moment the code can be redeemed; must be in the future when set. Tenant time in, UTC stored. | Optional |
reset_at | timestamp | When Reset last returned the code to active. | System |
bulk_id | char(10) | Generation batch: TP + 8 random upper-case characters, shared by every code in one generate call. | System |
quota_in, quota_out, session_time | bigint / int | Present on the row but not maintained by Kiwire 4 — usage is tracked on the account that redeemed the code. | — |
remark | char(120) | Free text (the form allows 255; the column keeps 120). | Optional |
creator, created_by, updated_by | char(64/50/50) | Admin usernames. | System |
Generate (POST /admin/accounts/topup-codes): plan_name, ncode (how many, 1–500), clength (random part length, 8–50), prefix (required; pre-filled from the Settings card's topup_prefix), price, quota (MB), minutes, date_expiry, remark. Codes are created synchronously, in one batch.
Redeem and Reset
| Action | Route | Effect |
|---|---|---|
| Redeem for account | POST topup-codes/{id}/redeem with username | The staff-side counterpart of the guest typing the code on the portal. Refused unless the code is active, unredeemed and unexpired and the target account (user or voucher on this tenant) is active. On success the code's time and quota are added to the account's profile_cus bonus credit, and the code gets status = used, username, date_activate. |
| Reset | POST topup-codes/{id}/reset | Puts the code back to active with username and date_activate cleared and reset_at stamped — and, if it had been redeemed, subtracts the same time and quota from that account's profile_cus again, so a reset cannot be used to credit an account twice. |
Both run in a transaction with the code and account rows locked.