Back to Blog

ActionType Explained: Making Business Ops Platform-Native

In traditional architectures, business operations are scattered across services:

CoomiaPublished on August 9, 202514 min read
Share this articleTwitter / X

ActionType Explained: Making Business Ops Platform-Native

Series: S4 Ontology Modeling · Article 5 | Level: Intermediate | Reading Time: 18 min

#TL;DR

  • ActionType elevates "business operations" from scattered code to first-class Schema citizens — approvals, assignments, computations, and notifications become discoverable, auditable, and reusable platform-native capabilities, ending the chaos of "business logic scattered across a hundred microservices."
  • 10 executor types (Function / Webhook / gRPC / Workflow / SQL / Script / Notification / Approval / Schedule / Composite) cover every scenario from simple field updates to multi-step approval flows.
  • Parameter validation + idempotency guarantees + audit logging form a triple defense line ensuring every operation is trustworthy, traceable, and safely retryable.

#1. Why ActionType Is Needed

In traditional architectures, business operations are scattered across services:

Code
Traditional Approach (Scattered Operations):

User clicks "Approve Order" →
  Frontend calls POST /api/orders/{id}/approve →
    Backend has a pile of logic →
      Update status + Send notification + Write log + Deduct inventory + ...

Problems:
├── Every operation is a custom API, no unified management
├── Parameter validation logic duplicated (frontend once, backend once)
├── No unified audit trail (some operations logged, some not)
├── Retry safety unknown (double-deducted inventory? double notifications?)
├── New team members can't see what business operations exist
└── Permission granularity at API level, not business operation level

coomia-dip Approach (ActionType):

User clicks "Approve Order" →
  SDK calls executeAction("ApproveOrder", params) →
    Platform handles uniformly:
    ├── 1. Parameter validation (Schema-defined rules)
    ├── 2. Permission check (RBAC + ABAC)
    ├── 3. Executor execution (one of 10 types)
    ├── 4. Audit logging (automatic)
    ├── 5. Idempotency check (prevent duplicate execution)
    └── 6. Event publishing (notify downstream systems)

#2. ActionType Data Model

#2.1 Core Definition

Python
from ontology_sdk import OntologyClient

client = OntologyClient(base_url="http://localhost:8080")

# Create a complete ActionType
action = client.schema.create_action_type({
    "apiName": "ApproveOrder",
    "displayName": "Approve Order",
    "description": "Manager approves pending orders; upon approval, auto-assigns warehouse and logistics",

    # Bound ObjectType
    "objectType": "Order",

    # Parameter definitions
    "parameters": {
        "orderId": {
            "type": "STRING",
            "required": True,
            "description": "Order ID",
        },
        "approved": {
            "type": "BOOLEAN",
            "required": True,
            "description": "Whether to approve",
        },
        "comment": {
            "type": "STRING",
            "required": False,
            "maxLength": 500,
            "description": "Approval comment",
        },
        "priority": {
            "type": "ENUM",
            "enumValues": ["LOW", "NORMAL", "HIGH", "URGENT"],
            "defaultValue": "NORMAL",
        },
    },

    # Executor configuration
    "executor": {
        "type": "FUNCTION",
        "functionId": "approve-order-fn",
        "timeout": 30000,  # 30-second timeout
        "retryPolicy": {
            "maxRetries": 3,
            "backoffMs": 1000,
        },
    },

    # Idempotency configuration
    "idempotency": {
        "enabled": True,
        "keyExpression": "orderId + '-' + executorUserId",
        "ttlSeconds": 3600,  # No re-execution within 1 hour
    },

    # Preconditions
    "preconditions": [
        {
            "type": "OBJECT_STATE",
            "field": "status",
            "operator": "eq",
            "value": "PENDING_APPROVAL",
            "errorMessage": "Only orders in PENDING_APPROVAL status can be approved",
        },
        {
            "type": "PERMISSION",
            "requiredRole": "ORDER_APPROVER",
        },
    ],

    # Side effects
    "sideEffects": [
        {
            "type": "UPDATE_OBJECT",
            "updates": {
                "status": "${approved ? 'APPROVED' : 'REJECTED'}",
                "approvedBy": "${currentUser.id}",
                "approvedAt": "${now()}",
            },
        },
        {
            "type": "SEND_NOTIFICATION",
            "template": "order-approval-result",
            "recipients": ["${object.createdBy}"],
        },
    ],
})

#2.2 Protobuf Schema

PROTOBUF
message ActionType {
    string api_name = 1;
    string display_name = 2;
    string description = 3;
    string object_type = 4;

    map<string, ParameterDef> parameters = 5;
    ExecutorConfig executor = 6;
    IdempotencyConfig idempotency = 7;

    repeated Precondition preconditions = 8;
    repeated SideEffect side_effects = 9;

    LifecycleState lifecycle = 10;
    AuditInfo audit = 11;
}

message ParameterDef {
    string type = 1;
    bool required = 2;
    string description = 3;
    string default_value = 4;
    repeated ValidationRule validations = 5;
}

message ExecutorConfig {
    ExecutorType type = 1;
    string function_id = 2;
    string webhook_url = 3;
    string grpc_service = 4;
    int32 timeout_ms = 5;
    RetryPolicy retry_policy = 6;
}

enum ExecutorType {
    FUNCTION = 0;
    WEBHOOK = 1;
    GRPC = 2;
    WORKFLOW = 3;
    SQL = 4;
    SCRIPT = 5;
    NOTIFICATION = 6;
    APPROVAL = 7;
    SCHEDULE = 8;
    COMPOSITE = 9;
}

#3. 10 Executor Types Explained

#3.1 FUNCTION — Function Executor

Code
The most common executor type, calling platform-registered Functions

┌──────────┐      ┌──────────────────┐      ┌──────────┐
│  Action  │─────►│ Function Runtime  │─────►│  Result  │
│  Request │      │  (Python/Java)    │      │          │
└──────────┘      └──────────────────┘      └──────────┘
Python
# Register a Function
client.functions.register({
    "functionId": "calculate-risk-score",
    "runtime": "python3.11",
    "handler": "risk_module.calculate_score",
    "timeout": 60000,
})

# Create an ActionType using the Function executor
client.schema.create_action_type({
    "apiName": "CalculateRiskScore",
    "objectType": "Customer",
    "parameters": {
        "customerId": {"type": "STRING", "required": True},
        "includeHistory": {"type": "BOOLEAN", "defaultValue": "true"},
    },
    "executor": {
        "type": "FUNCTION",
        "functionId": "calculate-risk-score",
    },
})

#3.2 WEBHOOK — HTTP Callback Executor

Python
# Call an external system's Webhook
client.schema.create_action_type({
    "apiName": "SyncToERP",
    "objectType": "Order",
    "executor": {
        "type": "WEBHOOK",
        "webhookUrl": "https://erp.company.com/api/sync-order",
        "method": "POST",
        "headers": {
            "Authorization": "Bearer ${secrets.ERP_TOKEN}",
            "Content-Type": "application/json",
        },
        "bodyTemplate": {
            "orderId": "${params.orderId}",
            "items": "${object.lineItems}",
        },
        "successCodes": [200, 201, 202],
        "timeout": 15000,
    },
})

#3.3 GRPC — gRPC Service Call

Python
# Call an internal gRPC service
client.schema.create_action_type({
    "apiName": "AllocateWarehouse",
    "objectType": "Order",
    "executor": {
        "type": "GRPC",
        "grpcService": "warehouse-service",
        "grpcMethod": "AllocateWarehouse",
        "protoMessage": "AllocateWarehouseRequest",
        "fieldMapping": {
            "order_id": "${params.orderId}",
            "region": "${object.shippingRegion}",
            "weight_kg": "${object.totalWeight}",
        },
    },
})

#3.4 WORKFLOW — Workflow Executor

Python
# Trigger a multi-step workflow (Temporal-based)
client.schema.create_action_type({
    "apiName": "OnboardNewEmployee",
    "objectType": "Employee",
    "executor": {
        "type": "WORKFLOW",
        "workflowId": "employee-onboarding",
        "taskQueue": "hr-workflows",
        "steps": [
            {"name": "create_accounts", "timeout": 60000},
            {"name": "assign_equipment", "timeout": 120000},
            {"name": "schedule_training", "timeout": 30000},
            {"name": "notify_team", "timeout": 10000},
        ],
    },
})

#3.5 SQL — Database Operation Executor

Python
# Execute SQL directly (suitable for batch updates)
client.schema.create_action_type({
    "apiName": "BatchUpdatePrices",
    "objectType": "Product",
    "executor": {
        "type": "SQL",
        "dataSource": "doris-main",
        "sqlTemplate": """
            UPDATE products
            SET price = price * (1 + :adjustmentPercent / 100.0),
                updated_at = NOW()
            WHERE category = :category
              AND price BETWEEN :minPrice AND :maxPrice
        """,
        "parameterMapping": {
            "adjustmentPercent": "${params.adjustmentPercent}",
            "category": "${params.category}",
            "minPrice": "${params.minPrice}",
            "maxPrice": "${params.maxPrice}",
        },
    },
})

#3.6 SCRIPT — Script Executor

Python
# Run a Python script snippet
client.schema.create_action_type({
    "apiName": "GenerateReport",
    "objectType": "Project",
    "executor": {
        "type": "SCRIPT",
        "language": "python",
        "script": """
import pandas as pd
from datetime import datetime

project = context.get_object("Project", params["projectId"])
tasks = context.traverse(project, "Project_hasTasks_Task")

df = pd.DataFrame([t.to_dict() for t in tasks])
summary = {
    "total": len(df),
    "completed": len(df[df.status == "DONE"]),
    "overdue": len(df[(df.status != "DONE") & (df.due_date < datetime.now())]),
    "completion_rate": f"{len(df[df.status == 'DONE']) / len(df) * 100:.1f}%",
}

context.update_object(project, {"lastReport": summary})
return {"success": True, "summary": summary}
        """,
        "timeout": 120000,
    },
})

#3.7 NOTIFICATION — Notification Executor

Python
# Send multi-channel notifications
client.schema.create_action_type({
    "apiName": "SendAlertNotification",
    "objectType": "Equipment",
    "executor": {
        "type": "NOTIFICATION",
        "channels": ["email", "sms", "webhook"],
        "template": "equipment-alert",
        "recipientExpression": """
            object.assignedTechnicians
            + object.factoryManager
        """,
        "variables": {
            "equipmentName": "${object.name}",
            "alertType": "${params.alertType}",
            "severity": "${params.severity}",
        },
    },
})

#3.8 APPROVAL — Approval Executor

Python
# Multi-level approval chain
client.schema.create_action_type({
    "apiName": "ApprovePurchaseRequest",
    "objectType": "PurchaseRequest",
    "executor": {
        "type": "APPROVAL",
        "approvalChain": [
            {
                "level": 1,
                "approverExpression": "${object.departmentManager}",
                "condition": "params.amount < 10000",
                "autoApproveAfterHours": 48,
            },
            {
                "level": 2,
                "approverExpression": "${object.financeDirector}",
                "condition": "params.amount >= 10000 && params.amount < 100000",
                "autoApproveAfterHours": 72,
            },
            {
                "level": 3,
                "approverExpression": "${getCEO()}",
                "condition": "params.amount >= 100000",
            },
        ],
        "onApproved": {
            "type": "FUNCTION",
            "functionId": "create-purchase-order",
        },
        "onRejected": {
            "type": "NOTIFICATION",
            "template": "purchase-rejected",
        },
    },
})

#3.9 SCHEDULE — Scheduled Executor

Python
# Scheduled or delayed execution
client.schema.create_action_type({
    "apiName": "ScheduleMaintenanceReminder",
    "objectType": "Equipment",
    "executor": {
        "type": "SCHEDULE",
        "scheduleExpression": "0 8 * * MON",  # Every Monday at 8 AM
        "action": {
            "type": "FUNCTION",
            "functionId": "check-maintenance-due",
        },
    },
})

#3.10 COMPOSITE — Composite Executor

Python
# Execute multiple sub-operations sequentially or in parallel
client.schema.create_action_type({
    "apiName": "ProcessNewOrder",
    "objectType": "Order",
    "executor": {
        "type": "COMPOSITE",
        "strategy": "SEQUENTIAL",  # SEQUENTIAL or PARALLEL
        "steps": [
            {
                "name": "validate_inventory",
                "executor": {"type": "FUNCTION", "functionId": "check-inventory"},
                "onFailure": "ABORT",  # Abort on failure
            },
            {
                "name": "charge_payment",
                "executor": {"type": "GRPC", "grpcService": "payment-service", "grpcMethod": "Charge"},
                "onFailure": "COMPENSATE",  # Compensate on failure
                "compensateAction": "RefundPayment",
            },
            {
                "name": "allocate_warehouse",
                "executor": {"type": "FUNCTION", "functionId": "allocate-warehouse"},
                "onFailure": "COMPENSATE",
                "compensateAction": "ReleaseWarehouse",
            },
            {
                "name": "notify_customer",
                "executor": {"type": "NOTIFICATION", "template": "order-confirmed"},
                "onFailure": "IGNORE",  # Notification failure doesn't block main flow
            },
        ],
    },
})

#4. Parameter Validation System

#4.1 Built-in Validation Rules

Python
# Rich parameter validation
client.schema.create_action_type({
    "apiName": "TransferFunds",
    "objectType": "Account",
    "parameters": {
        "sourceAccountId": {
            "type": "STRING",
            "required": True,
            "validations": [
                {"rule": "pattern", "value": "^ACC-[0-9]{10}$", "message": "Invalid account ID format"},
            ],
        },
        "targetAccountId": {
            "type": "STRING",
            "required": True,
            "validations": [
                {"rule": "pattern", "value": "^ACC-[0-9]{10}$"},
                {"rule": "notEqual", "referenceParam": "sourceAccountId", "message": "Cannot transfer to yourself"},
            ],
        },
        "amount": {
            "type": "DOUBLE",
            "required": True,
            "validations": [
                {"rule": "min", "value": 0.01, "message": "Amount must be greater than 0"},
                {"rule": "max", "value": 1000000, "message": "Single transfer cannot exceed 1,000,000"},
            ],
        },
        "currency": {
            "type": "ENUM",
            "enumValues": ["CNY", "USD", "EUR", "GBP", "JPY"],
            "defaultValue": "CNY",
        },
        "memo": {
            "type": "STRING",
            "required": False,
            "validations": [
                {"rule": "maxLength", "value": 200},
                {"rule": "noScript", "message": "Memo must not contain script code"},
            ],
        },
    },
})

#4.2 Custom Validation Functions

Python
# Register a custom validator
client.schema.create_action_type({
    "apiName": "ChangeEquipmentStatus",
    "objectType": "Equipment",
    "parameters": {
        "equipmentId": {"type": "STRING", "required": True},
        "newStatus": {"type": "ENUM", "enumValues": ["RUNNING", "MAINTENANCE", "STOPPED", "DECOMMISSIONED"]},
    },
    "validationFunction": "validate-status-transition",
})

# Validation function code
# def validate_status_transition(params, object, context):
#     valid_transitions = {
#         "RUNNING": ["MAINTENANCE", "STOPPED"],
#         "MAINTENANCE": ["RUNNING", "STOPPED"],
#         "STOPPED": ["RUNNING", "MAINTENANCE", "DECOMMISSIONED"],
#         "DECOMMISSIONED": [],  # Terminal state, no transitions
#     }
#     current = object.status
#     target = params["newStatus"]
#     if target not in valid_transitions.get(current, []):
#         raise ValidationError(f"Cannot transition from {current} to {target}")

#5. Idempotency Guarantees

#5.1 Idempotency Mechanism

Code
Idempotency guarantee flow:

Request arrives → Compute idempotency key → Query idempotency store
                                                    │
                                          ┌─────────┴─────────┐
                                          ▼                   ▼
                                     Key exists            Key absent
                                   (already executed)     (first execution)
                                          │                   │
                                          ▼                   ▼
                                    Return previous      Execute Action
                                    result                    │
                                                             ▼
                                                   Store idempotency key + result
                                                             │
                                                             ▼
                                                        Return result
Python
# Idempotent execution example
result1 = client.actions.execute("ApproveOrder", {
    "orderId": "order-001",
    "approved": True,
    "comment": "Approved",
}, idempotency_key="approval-order-001-user-admin")

# Same idempotency key — will not re-execute
result2 = client.actions.execute("ApproveOrder", {
    "orderId": "order-001",
    "approved": True,
    "comment": "Approved",
}, idempotency_key="approval-order-001-user-admin")

assert result1.execution_id == result2.execution_id  # Same execution
assert result2.was_deduplicated == True  # Marked as deduplicated

#5.2 Idempotency Key Strategies

Code
┌─────────────────────────────────────────────────────┐
│          Idempotency Key Generation Strategies       │
├─────────────────────────────────────────────────────┤
│                                                      │
│  1. Client-specified (most flexible):                │
│     idempotency_key = "user-123-approve-order-456"  │
│                                                      │
│  2. Parameter-derived (auto-computed):               │
│     keyExpression = "orderId + '-' + userId"         │
│     → "order-456-user-123"                           │
│                                                      │
│  3. Business-rule-based (semantic level):            │
│     keyExpression = "orderId + '-approve'"           │
│     → Same order can only be approved once           │
│                                                      │
│  TTL Strategies:                                     │
│  ├── Short-term (1 hour): Prevent double-clicks      │
│  ├── Medium-term (24 hours): Prevent same-day dupes  │
│  └── Permanent (no expiry): Business-once operations │
└─────────────────────────────────────────────────────┘

#6. Audit Logging

#6.1 Automatic Auditing

Python
# Every Action execution automatically generates an audit log
result = client.actions.execute("ApproveOrder", {
    "orderId": "order-001",
    "approved": True,
})

# Query audit logs
audit_logs = client.audit.query({
    "actionType": "ApproveOrder",
    "objectType": "Order",
    "objectId": "order-001",
    "timeRange": {"from": "2026-03-01", "to": "2026-03-24"},
})

for log in audit_logs:
    print(f"[{log.timestamp}] {log.user} executed {log.action_type}")
    print(f"  Parameters: {log.parameters}")
    print(f"  Result: {log.result_status}")
    print(f"  Duration: {log.duration_ms} ms")
    print(f"  Changes: {log.object_changes}")

#6.2 Audit Log Structure

Code
┌───────────────────────────────────────────────┐
│              Audit Log Entry                   │
├───────────────────────────────────────────────┤
│  execution_id    : "exec-uuid-001"            │
│  action_type     : "ApproveOrder"             │
│  object_type     : "Order"                    │
│  object_id       : "order-001"                │
│  user_id         : "user-admin"               │
│  timestamp       : "2026-03-24T10:30:00Z"     │
│  parameters      : {orderId, approved, ...}   │
│  result_status   : "SUCCESS"                  │
│  duration_ms     : 245                        │
│  idempotency_key : "approval-order-001-..."   │
│  was_deduplicated: false                      │
│  object_before   : {status: "PENDING"}        │
│  object_after    : {status: "APPROVED"}       │
│  side_effects    : ["notification-sent"]      │
│  ip_address      : "192.168.1.100"            │
│  user_agent      : "onto-sdk/1.0"             │
└───────────────────────────────────────────────┘

#7. Action Permission Control

Python
# ActionType permission model
client.schema.create_action_type({
    "apiName": "DeleteCustomer",
    "objectType": "Customer",
    "permissions": {
        # RBAC: Role-level permissions
        "requiredRoles": ["CUSTOMER_ADMIN"],

        # ABAC: Attribute-level permissions
        "attributeRules": [
            {
                "description": "Can only delete customers in own region",
                "expression": "user.region == object.region",
            },
            {
                "description": "VIP customers require director approval",
                "expression": "object.tier != 'VIP' || user.role == 'DIRECTOR'",
            },
        ],

        # Data-level permissions
        "fieldAccess": {
            "params.reason": "ALL",      # Everyone can provide a reason
            "params.force": "ADMIN_ONLY", # Only admins can force-delete
        },
    },
})

#8. Action Execution Lifecycle

Code
Action execution state machine:

┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│ SUBMITTED│───►│VALIDATING│───►│ EXECUTING│───►│COMPLETED │
└──────────┘    └─────┬────┘    └─────┬────┘    └──────────┘
                      │               │
                      ▼               ▼
               ┌──────────┐    ┌──────────┐
               │VALIDATION│    │  FAILED  │
               │_FAILED   │    │          │
               └──────────┘    └─────┬────┘
                                     │
                                     ▼
                              ┌──────────┐
                              │ RETRYING │──► EXECUTING (retry)
                              └──────────┘

Responsibilities at each stage:
├── SUBMITTED    → Accept request, assign execution_id
├── VALIDATING   → Parameter validation + precondition check + permission check
├── EXECUTING    → Invoke executor
├── COMPLETED    → Execution succeeded, trigger side effects
├── FAILED       → Execution failed, decide whether to retry based on policy
└── RETRYING     → Backoff wait, then re-execute
Python
# Async execution for long-running Actions
execution = client.actions.execute_async("OnboardNewEmployee", {
    "employeeId": "emp-new-001",
    "department": "engineering",
    "startDate": "2026-04-01",
})

print(f"Execution ID: {execution.id}")
print(f"Status: {execution.status}")  # SUBMITTED

# Poll execution status
import time
while execution.status not in ["COMPLETED", "FAILED"]:
    time.sleep(2)
    execution = client.actions.get_execution(execution.id)
    print(f"Status: {execution.status}, Progress: {execution.progress}%")

# Check execution result
if execution.status == "COMPLETED":
    print(f"Result: {execution.result}")
else:
    print(f"Error: {execution.error}")

#9. Batch Operations

Python
# Batch execute Actions
batch_result = client.actions.batch_execute("UpdateEquipmentStatus", [
    {"equipmentId": "equip-001", "newStatus": "MAINTENANCE"},
    {"equipmentId": "equip-002", "newStatus": "MAINTENANCE"},
    {"equipmentId": "equip-003", "newStatus": "MAINTENANCE"},
    {"equipmentId": "equip-004", "newStatus": "STOPPED"},
], batch_options={
    "concurrency": 5,           # Parallelism
    "stopOnFirstError": False,  # Don't stop on individual failure
    "transactional": False,     # Non-transactional (independent)
})

print(f"Total: {batch_result.total}")
print(f"Succeeded: {batch_result.success_count}")
print(f"Failed: {batch_result.failure_count}")

for failure in batch_result.failures:
    print(f"  {failure.params['equipmentId']}: {failure.error}")

#10. ActionType and Event-Driven Architecture

Python
# Action execution auto-publishes events
client.schema.create_action_type({
    "apiName": "UpdateInventory",
    "objectType": "Product",
    "executor": {
        "type": "FUNCTION",
        "functionId": "update-inventory",
    },
    "events": {
        "onSuccess": {
            "topic": "inventory.updated",
            "payload": {
                "productId": "${params.productId}",
                "oldQuantity": "${before.quantity}",
                "newQuantity": "${after.quantity}",
                "delta": "${params.quantityDelta}",
            },
        },
        "onFailure": {
            "topic": "inventory.update-failed",
            "payload": {
                "productId": "${params.productId}",
                "error": "${error.message}",
            },
        },
    },
})

# Other systems can subscribe to these events
client.events.subscribe("inventory.updated", handler=lambda event:
    print(f"Inventory changed: {event.productId} from {event.oldQuantity} to {event.newQuantity}")
)

#11. Real-World Case: Manufacturing Equipment Inspection

Python
# Complete equipment inspection ActionType

# 1. Start Inspection
client.schema.create_action_type({
    "apiName": "StartInspection",
    "objectType": "Equipment",
    "displayName": "Start Inspection",
    "parameters": {
        "equipmentId": {"type": "STRING", "required": True},
        "inspectorId": {"type": "STRING", "required": True},
        "inspectionType": {
            "type": "ENUM",
            "enumValues": ["ROUTINE", "SPECIAL", "EMERGENCY"],
            "required": True,
        },
    },
    "preconditions": [
        {"type": "OBJECT_STATE", "field": "status", "operator": "in", "value": ["RUNNING", "STOPPED"]},
    ],
    "executor": {
        "type": "COMPOSITE",
        "strategy": "SEQUENTIAL",
        "steps": [
            {
                "name": "create_inspection_record",
                "executor": {"type": "FUNCTION", "functionId": "create-inspection"},
            },
            {
                "name": "update_equipment_status",
                "executor": {
                    "type": "SQL",
                    "sqlTemplate": "UPDATE equipment SET inspection_status = 'IN_PROGRESS' WHERE id = :equipmentId",
                },
            },
            {
                "name": "notify_maintenance_team",
                "executor": {"type": "NOTIFICATION", "template": "inspection-started"},
                "onFailure": "IGNORE",
            },
        ],
    },
    "idempotency": {
        "enabled": True,
        "keyExpression": "equipmentId + '-' + inspectorId + '-' + today()",
        "ttlSeconds": 86400,
    },
})

# 2. Record Inspection Result
client.schema.create_action_type({
    "apiName": "RecordInspectionResult",
    "objectType": "Equipment",
    "displayName": "Record Inspection Result",
    "parameters": {
        "inspectionId": {"type": "STRING", "required": True},
        "overallStatus": {"type": "ENUM", "enumValues": ["PASS", "WARNING", "FAIL"], "required": True},
        "checkItems": {
            "type": "ARRAY",
            "itemType": "STRUCT",
            "structFields": {
                "itemName": {"type": "STRING"},
                "result": {"type": "ENUM", "enumValues": ["OK", "ABNORMAL", "CRITICAL"]},
                "note": {"type": "STRING"},
                "photoUrls": {"type": "ARRAY", "itemType": "STRING"},
            },
        },
        "recommendations": {"type": "STRING", "maxLength": 2000},
    },
    "executor": {
        "type": "FUNCTION",
        "functionId": "process-inspection-result",
    },
})

# Usage example
result = client.actions.execute("StartInspection", {
    "equipmentId": "equip-CNC-001",
    "inspectorId": "tech-zhang",
    "inspectionType": "ROUTINE",
})
print(f"Inspection started: {result.data['inspectionId']}")

#Key Takeaways

  1. ActionType is the Schema-level expression of business operations — every operation has explicit parameters, executors, permissions, and audit trails, making business capabilities discoverable, manageable, and reusable.
  2. 10 executor types cover everything from simple function calls to complex multi-step approval flows, with the COMPOSITE executor supporting Saga-pattern distributed transaction compensation.
  3. Parameter validation is pushed to the Schema layer — type checking, range validation, regex matching, and custom validation functions, defined once and enforced uniformly across frontend and backend.
  4. Idempotency is a hard requirement for production systems — through idempotency keys and TTL mechanisms, network jitter and user retries are guaranteed to never cause duplicate execution.
  5. Full audit logs are auto-generated — who did what to which object at what time, with complete input/output records, forming the foundation for compliance and troubleshooting.

#Next Article

The next article, S4-06 InterfaceType and StructType, will explore advanced features of the Ontology type system — how interface inheritance enables polymorphic queries and how struct nesting builds complex value objects.

#ontology #action-type #executor #idempotency #audit-log #parameter-validation #workflow