Skip to content

Workflow API

Customer password recovery adds the CRM trigger customer.password_reset_requested and the subject-specific action customer.password_reset.request_delivery. Workflow selects a logical delivery_route; external modules own templates and channels. See the complete recovery contract, including conditions, the single-action rule constraint and first-stage limitations.

Workflow API manages flat automation chains:

trigger -> conditions -> actions

Workflow chains are stored as WorkflowRuleRecord. Current API covers definitions, validation, CRUD, run log reads and runtime execution for trade, account, customer and KYC events.

Runtime

The runtime path is event-driven:

EventBus trade.push / account.push / customer.push / kyc.step.push
  -> WorkflowManager
  -> ThreadPoolGlobal worker
  -> WorkflowRuntimeContext
  -> active rules by resolved trading group brand + trigger
  -> trade/account/customer conditions
  -> runtime-supported actions
  -> workflow run log

Supported trigger mapping:

EV_RECORD_ADD + OP_BUY/OP_SELL            -> trade.opened
EV_RECORD_ACTIVATE_TRADE + OP_BUY/OP_SELL -> trade.opened
EV_RECORD_CLOSE_TRADE + OP_BUY/OP_SELL    -> trade.closed
EV_RECORD_ADD + OP_BALANCE_IN             -> deposit.created
EV_RECORD_ADD + OP_BALANCE_OUT            -> withdrawal.created
EV_RECORD_ADD + OP_CREDIT_IN              -> credit.created
account.push + EV_RECORD_ADD              -> account:added
account.push + EV_RECORD_UPDATE           -> account:updated
account.push + EV_RECORD_DELETE           -> account:deleted
customer.push + EV_RECORD_ADD             -> customer.created
customer.push + EV_RECORD_UPDATE          -> customer.updated
kyc.step.push + approved status           -> kyc.step.approved
kyc.step.push + rejected status           -> kyc.step.rejected
kyc profile submission                    -> kyc.profile.submitted

Rule order and customer data

Several rules can match one event. They run one after another:

  • Rules are ordered by priority descending, then by id descending.
  • A failed rule does not stop the chain. A rule with stop_processing = 1 stops the rules after it, but only when it succeeded.
  • The conditions of every rule are evaluated against the event snapshot: the customer as it was when the event was raised. A change made by an earlier rule for the same event does not change which later rules match.
  • An action that changes the customer writes only its own fields. The server takes the current customer record under the customer lock, applies the change and stores it in one step. Changes made by earlier rules for the same event — desk, assigned manager, statuses, risk level, deposit and withdrawal permissions, lifecycle flags, customer kind — are kept, and so are changes made at the same time by other sources, such as a manager editing the customer in CRM or another event.
  • Actions that read customer data, such as notify.manager without manager_id or notify.send, use the event snapshot together with the results of the customer changes already made for this event. A rule that runs after customer.attach_manager notifies the newly attached manager. Changes made by other sources after the event was raised are not visible to these actions.
  • input_json in the run log keeps the event snapshot the conditions were evaluated against.

An action whose value is already set — the same status, risk level, desk, assigned manager, deposit or withdrawal permission, lifecycle flags or customer kind — does not write the customer and does not emit an update, so it does not raise customer.updated again. Such an action is not counted in executed_actions of the run result.

Supported runtime conditions include:

brand
amount_usd
trade.order
trade.login
trade.cmd
trade.volume
trade.close_price
trade.symbol
trade.comment
account.login
account.group
account.name
account.email
account.country
account.enable
account.leverage
customer.country
customer.language
customer.email
customer.kyc_status
customer.kyc_current_level
customer.risk_level
customer.lifecycle_flags
customer.kind
customer.created_by_import

Supported runtime actions:

notify.manager
customer.set_risk_level
customer.set_status
customer.set_lifecycle_flags
kyc.require_step
kyc.set_required_level
webhook.send
customer.email_template.request_delivery

Declared without a runtime branch:

note.add
tag.add
calendar.create_task

These actions are returned by MngGetWorkflowActionsByTrigger and pass rule validation, but a run that reaches one of them fails with UNSUPPORTED_RUNTIME_ACTION: <type>. Actions placed before it in the same rule have already run.

customer.email_template.request_delivery, formerly email.send_template, hands delivery of a mailer template to the linked customer over to the mailer module: the server does not send email itself, it publishes customer.email_template.delivery_requested and the mailer renders and sends the template. The action is offered on customer.created, deposit.created, withdrawal.created, kyc.step.approved, kyc.step.rejected, cashier.deposit.approved, account.margin_call.alert and credit.created; cashier.withdrawal.approved does not offer it. On a cashier deposit, a margin call or a credit the event carries only the customer snapshot, not the deposit amount, account login, margin values or credit amount; on account.margin_call.alert it fires on every entry into Margin Call. Rules that used the old code are archived on upgrade.

template (required) and fallback_template (optional) are mailer template ids, integers greater than zero. Rule validation rejects other values with EMAIL_TEMPLATE_REQUIRES_VALID_TEMPLATE or EMAIL_TEMPLATE_REQUIRES_VALID_FALLBACK_TEMPLATE. A run fails with CUSTOMER_REQUIRED_FOR_ACTION: customer.email_template.request_delivery when the event has no linked customer. Contacts are not checked; to skip customers without an address, use the customer.email condition with exists, available on all eight triggers of the action.

{
  "type": "customer.email_template.request_delivery",
  "params": {
    "template": 12,
    "fallback_template": 4
  }
}

If notify.manager.params.manager_id is omitted or 0, backend first tries the linked customer's assigned_manager_id, then manager_id, then broadcasts to enabled managers.

webhook.send requires params.url and sends an outbound HTTP request from workflow runtime. Optional params are method, headers, body, and timeout_ms.

{
  "type": "webhook.send",
  "params": {
    "url": "https://example.com/webhook",
    "method": "POST",
    "headers": {
      "Content-Type": "application/json"
    },
    "body": "{\"text\":\"High risk trade\"}",
    "timeout_ms": 5000
  }
}

method defaults to POST; timeout_ms is clamped to 1000..30000. The action succeeds only on HTTP 2xx; curl errors or non-2xx responses make the workflow run failed. Runtime logs mask common webhook tokens.

kyc.require_step creates or reuses a customer KYC profile and attaches a required step from a KYC step template. Required params are step_code; optional params are level and reason.

kyc.set_required_level creates or reuses a customer KYC profile and increases the required KYC level. Required param is level; optional param is reason. When the level is increased, KycManager attaches all active required templates for the same brand with template.level <= level.

customer.set_lifecycle_flags explicitly raises and clears milestone bits on the linked customer. It takes params.add and params.remove, each in range 0..63; both are optional individually, but at least one must be present. KYC and Cashier events do not raise a flag implicitly; a flag changes only when this action is present in an active matching rule. Flag values are listed in Lifecycle Flags.

Conditions on customer.lifecycle_flags use the bits type and the operators has_all, has_any, and has_none; the value is a single integer holding the selected bits. customer.kind is an ordinary integer condition.

Example: record the first deposit for a customer that is not marked yet:

{
  "trigger": "deposit.created",
  "conditions": [
    {"field": "customer.lifecycle_flags", "op": "has_none", "value": 8}
  ],
  "actions": [
    {
      "type": "customer.set_lifecycle_flags",
      "params": {"add": 8}
    }
  ]
}

KYC workflow actions require a linked customer context. They are executed through KycManager; the trading mainLoop is not involved.

For a deposit-triggered KYC flow, create a rule on deposit.created with conditions such as amount_usd >= 1 and customer.kyc_current_level < 1, then execute kyc.set_required_level with level = 1.

trade.volume is integer volume: 100 = 1.00 lot, so 10 lots is 1000.

Brand is resolved through the trading group:

TradeRecord.login -> AccountRecord.group -> GroupRecord.brand

This supports both CRM accounts linked to customers and direct BO/MT-style accounts without CRM customers. CustomerRecord.brand remains CRM metadata; trade workflow brand scope comes from the trading group. If no group brand can be resolved, the workflow is skipped and a warning is written to logs.

Access Model

Only SESSION_ADMIN can create, update, archive or delete workflow chains:

MngAddWorkflowRule
MngUpdateWorkflowRule
MngArchiveWorkflowRule
MngDeleteWorkflowRule

Read, metadata and validation methods are available to manager sessions:

SESSION_MANAGER
SESSION_ADMIN
SESSION_DEALER
SESSION_CRM_MANAGER
SESSION_CRM_ADMIN

SEO

Workflow API supports CRM workflow automation, BackOffice automation, broker rule engine, KYC automation, customer lifecycle automation and trigger condition action workflows.