The Approval Engine

Approvals in AI GO are not an "approvals module" — they are an interceptor sitting on the data write path. Which means: whether data arrives from a Custom App, the REST API, or the AI assistant, approvals apply. There is no route around them.


How it works

In a typical system, creating a leave request or purchase order writes immediately. AI GO differs:

  1. Write interception — once an administrator enables an approval workflow on a table, writes to it are intercepted
  2. Assignment — the platform derives qualified approvers from the workflow definition, notifies them, and creates an approval request
  3. Approval callback — when every stage passes, the platform executes the originally intercepted operation, including all the business engines it cascades into (invoicing, stock deduction, journal entries)

A rejection at any stage returns the record to draft for revision and resubmission.

The three write types are intercepted differently

OperationInterception behavior
InsertThe record is held pending and takes effect on approval
UpdateNot executed. The payload is held and applied automatically on approval
DeleteNot executed. Performed automatically on approval

The distinction matters: updates and deletes are not written and then rolled back — they simply do not run. The caller receives a pending-approval exception carrying a request identifier.

Callers should not retry — retrying only creates duplicate requests. Catch the exception and tell the user the change was submitted for approval.


Configuring a workflow

Entry point: Dashboard → System & Operations → Approval Workflow Engine. Requires system.admin.

1. Choose the target

Select the table the workflow applies to (sales orders, leave requests, vendor bills, and so on). One table can have only one active workflow.

2. Trigger conditions

Approvals can be required only under specific conditions — "purchase orders above 100,000 require manager sign-off", for instance. Anything below the threshold passes straight through, so you don't manufacture unnecessary approval load.

Conditions can also route to different flows based on record field values.

3. Define stages

Create ordered stages (department manager first, finance director second). Each stage can specify:

  • Whether it is required
  • Approval type — one approver suffices, or all listed approvers must sign (parallel approval)

4. Define qualified approvers

Each stage specifies approver eligibility, with three rule types:

RuleNotes
Specific userA named account
RoleEveryone holding that role (all finance managers, say)
Dynamic department managerResolved from the requester's department manager, with a configurable number of levels upward

Dynamic department manager is the most practical — org changes don't require revisiting workflow definitions.


Integrating into a Custom App

The approval SDK lets you build approvals directly into an application so users never switch to the Dashboard.

Server side

def execute(ctx):
    # The triggering user's own queue
    pending = ctx.approval.list_pending()

    # A record's approval state, with per-stage detail
    status = ctx.approval.get_record_status('sale_orders', order_id)

    # Approve / reject as the triggering user
    ctx.approval.approve(line_id, comment='Verified')
    ctx.approval.reject(line_id, comment='Amount does not match the quote')

    # Cancel a request (requester only, while pending)
    ctx.approval.cancel(request_id, reason='Re-evaluating')

Front end

src/approval.ts provides the matching front-end methods — enough to assemble an approval panel showing history, accepting a comment, and approving or rejecting in one click.

Identity binding

The approval SDK is bound to user identity, which sets it apart from the other SDKs:

  • list_pending() returns the triggering user's own queue, not the organization's
  • approve() verifies the triggering user is a qualified approver for that stage and refuses otherwise
  • Calls without a platform user identity (such as schedules) are rejected

You cannot use an application's privileges to approve on someone else's behalf.


Integrating via API

External systems can integrate the full mechanism:

GET  /api/v1/approvals/record/{res_model}/{res_id}
POST /api/v1/approvals/{request_line_id}/approve
POST /api/v1/approvals/{request_line_id}/reject
POST /api/v1/approvals/{request_id}/retry

Status query returns each stage's state, approver name, comment, and timestamp, or empty if no approval was initiated.

Approve takes an approval line identifier and a comment. The system verifies eligibility; the final stage triggers the callback automatically.

Reject resets the record to draft for revision and resubmission.

Retry callback is for the case where approval fully passed but the callback failed for external reasons. An administrator can retry it manually. A failed callback does not roll the approval state back — it is an independent remediation.


Common design patterns

Tell users "submitted for approval", not "failed"

def execute(ctx):
    try:
        ctx.db.update('sale_orders', order_id, {'state': 'sale'})
        return ctx.response.json({'status': 'updated'})
    except Exception as e:
        if 'approval' in str(e).lower():
            return ctx.response.json({
                'status': 'pending_approval',
                'message': 'Change submitted for approval; it will apply once approved'
            })
        raise

Show approval state in lists

Batch-query with get_record_status and surface "stage 2 of 2 — finance director" directly in the list, so users don't have to open each record.

A manager's task panel

Use list_pending() to build a to-do block on the application's home screen. This is the approval SDK's highest-value use — managers never log into a separate system to sign off.


Boundaries and caveats

  • One workflow per table — use conditional routing rather than multiple workflows when different paths are needed
  • Ordering relative to business engines — callbacks run after approval, so invoicing, stock deduction, and journal entries happen at approval time, not at submission
  • Scheduled writes — approval guards still apply; the approval UI shows the requester as "API"
  • Auditing — every approval decision is fully recorded, including approver, timestamp, and comment