JSON API Contract
Contract at a glance
The declared API surface is handled by Api in imspr/novusoft/modules/Api/Controllers/Api.php, with session and module data supplied by ApiModel in imspr/novusoft/modules/Api/Models/ApiModel.php and credential validation supplied by AuthModel in imspr/novusoft/modules/Auth/Models/AuthModel.php.
Routes map /api to Api::index and one-segment paths under /api to controller method names. Auto-routing is also enabled. These are source-declared routes; deployment reachability and exclusive HTTP method enforcement are not established by the source.
sequenceDiagram
participant Client
participant Login as Api::login
participant Auth as AuthModel::authCredentials
participant Session as ApiModel::createsession
participant Record as ApiModel::getrecord
participant Module as Api::module
participant Fields as Api::fields
participant Guard as ApiModel::isLogin
participant ModulePanel as ApiModel::getModulePanel
participant Open as Api::open
participant Editor as Component::editor
participant Out as Api::out
Client->>Login: POST /api/login: username and password
Login->>Auth: authCredentials(access)
alt credentials succeed
Auth-->>Login: success session data
Login->>Session: createsession(user_id)
Session-->>Login: sessionid and expired
Login->>Record: getrecord(brgy)
Record-->>Login: household and individual records
Login->>Out: out(data): credential and record
Out-->>Client: success result
else credentials fail
Auth-->>Login: failure message
Login->>Out: out(data): success false and message
Out-->>Client: failure result
end
Client->>Module: POST /api/module
Module->>Guard: isLogin(access)
Guard-->>Module: role id or false
alt session valid
Module->>ModulePanel: getModulePanel(module)
ModulePanel-->>Module: module and panel structure
Module->>Out: out(data): module result
Out-->>Client: module result
else session invalid
Module->>Out: out(data): session-expired result
Out-->>Client: session-expired result
end
Client->>Fields: POST /api/fields
Fields->>Guard: isLogin(access)
Guard-->>Fields: role id or false
alt session valid
Fields->>Fields: xfields(input)
Fields-->>Fields: field metadata
Fields->>Out: out(data): fields result
Out-->>Client: fields result
else session invalid
Fields->>Out: out(data): session-expired result
Out-->>Client: session-expired result
end
Client->>Open: POST /api/open
Open->>Guard: isLogin(access)
Guard-->>Open: role id or false
alt session, parameters, and action permission valid
Open->>Fields: fields(input)
Fields-->>Open: field metadata
Open->>Editor: editor(action, panel_id, primary_id)
Editor-->>Open: editor HTML
Open->>Out: out(data): fields and html_format
Out-->>Client: editor result
else validation, permission, or session failure
Open->>Out: out(data): failure result
Out-->>Client: failure result
end
[!WARNING] The shared Api::out helper returns JSON with HTTP status
200, setsAccess-Control-Allow-Originto*, allows all request headers, and advertisesPOST. The generic route mappings and auto-routing do not prove that requests must usePOSTor that the endpoints are reachable in a deployed environment.Startup protection is conditional on module/backend state or a first URI segment equal to
json. Api::item_list, Api::item_category, and Api::expence_account have no controller-local ApiModel::isLogin check. Api::index initializes fixedaccess.user_idandaccess.session_idfields before its authentication call; that call is commented out, so its active user-count query is not documented as authenticated.The login path returns authentication failure messages, and the inspected authentication path has no source-proven throttling, rate limiting, or lockout control.
ApiModel::getModulePanel returns
falsewhen module data is absent, while the module and editor paths dereference returned module-panel fields. Missing or deployment-specific module data therefore does not have a proven stable error envelope.
Declared endpoints
Use this comparison for the declared request and response shapes. The detailed endpoint contracts follow it.
| Endpoint | Method | Request fields | Response shape |
|---|---|---|---|
/api |
POST | None | JSON result from the user-count query |
/api/login |
POST | JSON: username, password |
Keyed success/message result; success includes credential data and a brgy-scoped record |
/api/loginold |
POST | JSON: username, password |
Keyed success/message result with a distinct legacy success payload |
/api/module |
POST | JSON: access.user_id, access.session_id, module.name |
Keyed module result or session-expired result |
/api/fields |
POST | JSON: access.user_id, access.session_id, module.name |
Keyed field-metadata result or session-expired result |
/api/open |
POST | JSON: access.user_id, access.session_id, module.name, module.action, module.panel_id, and module.primary_id for non-add actions |
Keyed fields/editor result, validation failure, permission denial, or session-expired result |
/api/item_list |
POST | Form fields: year, category, expence_account |
Raw selected item rows |
/api/item_category |
POST | None | Raw distinct normalized category values |
/api/expence_account |
POST | None | Raw account_code and account_desc rows |
API index
{
"title": "API index",
"description": "Declared POST JSON endpoint for Api::index; the active implementation returns the tbl_users count through the shared response helper without the commented authentication call.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "none",
"requestBody": "",
"formData": [],
"rawBody": "",
"responses": {
"200": {
"description": "JSON result from Api::index through the shared response helper.",
"body": ""
}
}
}
Login
{
"title": "Login",
"description": "Declared POST JSON login endpoint. The request supplies username and password; successful source paths create a session and return credential data plus a brgy-scoped record, while failure returns the AuthModel message.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api/login",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "json",
"requestBody": "{\"username\":\"<username>\",\"password\":\"<password>\"}",
"formData": [],
"rawBody": "",
"responses": {
"200": {
"description": "JSON login result containing source-defined success data or authentication failure message.",
"body": ""
}
}
}
Legacy login
{
"title": "Legacy login",
"description": "Declared POST JSON endpoint for the separate loginold handler, accepting the same source-defined username and password fields but using a distinct legacy success-payload path.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api/loginold",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "json",
"requestBody": "{\"username\":\"<username>\",\"password\":\"<password>\"}",
"formData": [],
"rawBody": "",
"responses": {
"200": {
"description": "JSON result from the legacy login handler through the shared response helper.",
"body": ""
}
}
}
Module
{
"title": "Module",
"description": "Declared POST JSON endpoint accepting access and module.name and returning the source-defined module/panel structure when the handler's success path is reached.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api/module",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "json",
"requestBody": "{\"access\":{\"user_id\":\"<user_id>\",\"session_id\":\"<session_id>\"},\"module\":{\"name\":\"<module>\"}}",
"formData": [],
"rawBody": "",
"responses": {
"200": {
"description": "JSON module result or source-defined session-expired result.",
"body": ""
}
}
}
Fields
{
"title": "Fields",
"description": "Declared POST JSON endpoint accepting access and module.name and returning field metadata built by xfields on its success path.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api/fields",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "json",
"requestBody": "{\"access\":{\"user_id\":\"<user_id>\",\"session_id\":\"<session_id>\"},\"module\":{\"name\":\"<module>\"}}",
"formData": [],
"rawBody": "",
"responses": {
"200": {
"description": "JSON field metadata result or source-defined session-expired result.",
"body": ""
}
}
}
Open editor
{
"title": "Open editor",
"description": "Declared POST JSON endpoint accepting access and module action/panel identifiers. It returns field metadata and editor HTML only after the source-defined parameter and permission branches pass.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api/open",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "json",
"requestBody": "{\"access\":{\"user_id\":\"<user_id>\",\"session_id\":\"<session_id>\"},\"module\":{\"name\":\"<module>\",\"action\":\"<action>\",\"panel_id\":\"<panel_id>\",\"primary_id\":\"<primary_id>\"}}",
"formData": [],
"rawBody": "",
"responses": {
"200": {
"description": "JSON editor result, validation failure, permission denial, or session-expired result.",
"body": ""
}
}
}
Item list
{
"title": "Item list",
"description": "Declared POST JSON response endpoint using arranged year, optional category, or expense-account inputs to return selected item rows.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api/item_list",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "form",
"requestBody": "",
"formData": [
{
"key": "year",
"value": "<year>",
"required": false
},
{
"key": "category",
"value": "<category>",
"required": false
},
{
"key": "expence_account",
"value": "<expence_account>",
"required": false
}
],
"rawBody": "",
"responses": {
"200": {
"description": "Raw selected item result data rather than a success/message envelope.",
"body": ""
}
}
}
Item categories
{
"title": "Item categories",
"description": "Declared POST JSON response endpoint returning distinct normalized category values from tbl_item_list.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api/item_category",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "none",
"requestBody": "",
"formData": [],
"rawBody": "",
"responses": {
"200": {
"description": "Raw category result data rather than a success/message envelope.",
"body": ""
}
}
}
Expense accounts
{
"title": "Expense accounts",
"description": "Declared POST JSON response endpoint returning account_code and account_desc rows from tbl_expence_account.",
"method": "POST",
"baseUrl": "",
"endpoint": "/api/expence_account",
"headers": [],
"queryParams": [],
"pathParams": [],
"bodyType": "none",
"requestBody": "",
"formData": [],
"rawBody": "",
"responses": {
"200": {
"description": "Raw expense-account result data rather than a success/message envelope.",
"body": ""
}
}
}
Endpoint-specific behavior
[
{
"title": "Login payload and session",
"body": "**Api::login** passes `username` and `password` to **AuthModel::authCredentials**. **AuthModel::authCredentials** looks up a non-deleted user and role by login email or username, hashes the supplied password with the stored hash key, and requires both status values to be truthy. Failures use `Invalid Username or Password`, or `Account/Role disabled` when the password matches but a status check fails. The inspected authentication path has no source-proven throttling, rate limiting, or lockout control. On the current success path, **ApiModel::createsession** generates a session identifier, stores it for the user, and sets `expired` three hours from the current time. The response credential contains identity, session, expiry, and `brgy` data; `record` is populated from the brgy-scoped household and individual lookup. The legacy **Api::loginold** handler uses a distinct success-payload path."
},
{
"title": "Module response",
"body": "After **ApiModel::isLogin** returns a role id, **Api::module** calls **ApiModel::getModulePanel** and assigns its `out` value to `module`. The module structure contains module metadata and panel entries; panel identifiers are encoded before being returned. **ApiModel::isLogin** decodes `access.user_id` and checks the matching `session_id` against the current time and stored expiry. A failed check returns the source-defined `Session expired please login` result."
},
{
"title": "Field metadata",
"body": "**Api::fields** uses the same access/session validation and passes the request to `xfields`. Each generated field entry contains name, label, property metadata, display metadata, value, and option information. The success path assigns this result to `fields`; clients should not infer a uniform success envelope from the raw field assignment."
},
{
"title": "Editor request",
"body": "**Api::open** validates `module.action` and `module.panel_id`. It additionally requires `module.primary_id` for every action other than `add`. Failed checks return `success: false` with a parameter-specific message. After the session, module, and action-permission branches pass, the handler assigns field metadata and passes `action`, `panel_id`, and the optional `primary_id` to **Component::editor** as `html_format`. A false action permission returns `Permission Denied`. A valid session and action permission do not, by themselves, establish ownership of the selected `primary_id`."
},
{
"title": "Item list",
"body": "**Api::item_list** arranges POST input and selects item rows containing `item_id`, `new_item_description`, `year`, `item_code`, `category`, `new_unit`, `year_amount`, and `modep`. When `category` is present, the query uses `year` and category. Otherwise it uses `year` and parameterized `FIND_IN_SET` membership for `expence_account`. The handler returns the query's raw `data` value."
},
{
"title": "Item categories",
"body": "**Api::item_category** selects distinct category values from `tbl_item_list` after removing CRLF sequences and returns the raw result data."
},
{
"title": "Expense accounts",
"body": "**Api::expence_account** selects `account_code` and `account_desc` from `tbl_expence_account` and returns the raw result data."
}
]
Updated