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, sets Access-Control-Allow-Origin to *, allows all request headers, and advertises POST. The generic route mappings and auto-routing do not prove that requests must use POST or 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 fixed access.user_id and access.session_id fields 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 false when 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