Rules & Watchers
Implement programmable governance and automated business logic.
Rules & Watchers
Beetles includes a powerful governance layer that allows you to define programmable business logic that is enforced on every transaction. This is achieved through two main components: Rules and Watchers.
Watchers
A Watcher is a monitoring point that "watches" for transactions on a specific resource. You can attach watchers to:
- Ledgers: Applies to all transfers within that ledger.
- Accounts: Applies to any transfer where the account is the debit (source) or credit (destination).
- Identities: Applies to any transfer involving accounts linked to that identity.
When a transfer is initiated on a resource with an active watcher, Beetles automatically switches to a Governance Workflow.
Watcher Configuration
Each watcher has two configurable properties:
| Field | Type | Default | Description |
|---|---|---|---|
threshold | integer | 60 | Minimum governance score required to approve the transfer. |
mode | string | "enforce" | "enforce" blocks transfers below threshold. "observe" always approves but logs the score for analysis. |
Observe mode is useful when you want to analyze transaction patterns before enabling enforcement — watchers in this mode act as labelers, tagging transfers with their governance scores without blocking any of them.
Creating a Watcher
curl -X POST /v1/watchers \
-H "Content-Type: application/json" \
-d '{
"target_type": "account",
"target_id": "01961234-abcd-7000-0000-000000000001",
"threshold": 60,
"mode": "observe",
"rule_ids": [
"01961234-abcd-7000-0000-000000000010",
"01961234-abcd-7000-0000-000000000011"
]
}'You can optionally pass
rule_idsat creation time to attach rules in a single request, or attach them later viaPUT /watchers/{id}/rules/{ruleId}.
Governance Relationship Model
The execution follows a strict hierarchical evaluation path when a transfer occurs:
Rules
A Rule is a set of conditions that must be met for a transaction to be approved. Rules are attached to Watchers.
Each rule has:
- Conditions: A list of structured conditions to evaluate.
- Logical Operator: How conditions are combined —
and(all must pass) oror(any must pass). - Weight: A penalty value subtracted from the initial score (100) if the rule fails.
- Priority: The order in which rules are evaluated.
The Scoring System
Beetles uses a score-based approval system:
Transfer starts with score of 100
Every transfer begins with a perfect governance score.
All matching rules are evaluated
Rules attached to the triggered watcher are evaluated in priority order.
Failed rules subtract their weight
If a rule's conditions fail, its weight is subtracted from the score.
Score compared to watcher threshold
If the final score is above the watcher's threshold (default: 60), the transfer is approved. In enforce mode, transfers below threshold are voided. In observe mode, transfers are always approved but the score is logged for analysis.
Conditions
Each condition evaluates a single field against a value using an operator.
Structure
{
"field_type": "native",
"native_field": "amount",
"operator": "<",
"value": "1000000"
}Available Operators
| Operator | Description |
|---|---|
> | Greater than |
< | Less than |
>= | Greater than or equal |
<= | Less than or equal |
== | Equal |
!= | Not equal |
Native Fields
You can evaluate core transaction data and source/destination account statistics:
| Field | Description |
|---|---|
amount | The value of the transfer in cents. |
code | The transfer code (user-defined). |
created_at | Transfer timestamp (Unix seconds). |
source.id | Source account ID. |
source.name | Source account name. |
source.velocity_1h | Transfer count from source account in the last hour. |
source.daily_total | Total value transferred from source in the last 24h. |
source.monthly_total | Total value transferred from source in the last 30 days. |
source.identity_id | ID of the identity linked to the source. |
source.identity_label | Label of the identity linked to the source. |
destination.id | Destination account ID. |
destination.name | Destination account name. |
destination.velocity_1h | Transfer count to destination account in the last hour. |
destination.daily_total | Total value transferred to destination in the last 24h. |
destination.monthly_total | Total value transferred to destination in the last 30 days. |
destination.identity_id | ID of the identity linked to the destination. |
destination.identity_label | Label of the identity linked to the destination. |
Metadata Fields
You can also evaluate values from the transaction metadata by referencing a Metadata Field mapping by its ID. See Metadata Mapping for details.
{
"field_type": "metadata",
"metadata_field_id": "01961234-abcd-7000-0000-000000000001",
"operator": ">",
"value": "75"
}Creating a Rule
Rules are created with one or more conditions. Use logical_operator to control how they combine.
Single Condition (AND default)
curl -X POST /v1/rules \
-H "Content-Type: application/json" \
-d '{
"name": "Max Transfer Limit",
"logical_operator": "and",
"weight": 30,
"priority": 1,
"conditions": [
{
"field_type": "native",
"native_field": "amount",
"operator": "<",
"value": "1000000"
}
]
}'Multiple Conditions (AND)
All conditions must pass for the rule to pass:
curl -X POST /v1/rules \
-H "Content-Type: application/json" \
-d '{
"name": "Large + Fast Combo",
"logical_operator": "and",
"weight": 50,
"priority": 0,
"conditions": [
{
"field_type": "native",
"native_field": "source.velocity_1h",
"operator": "<",
"value": "20"
},
{
"field_type": "native",
"native_field": "amount",
"operator": "<",
"value": "2500000"
}
]
}'Multiple Conditions (OR)
Any condition passing is sufficient:
{
"name": "Source or Destination Cap",
"logical_operator": "or",
"weight": 25,
"priority": 2,
"conditions": [
{
"field_type": "native",
"native_field": "source.daily_total",
"operator": "<",
"value": "10000000"
},
{
"field_type": "native",
"native_field": "destination.daily_total",
"operator": "<",
"value": "10000000"
}
]
}Two-Phase Governance
When a Watcher is triggered, Beetles performs a Two-Phase Enforcement:
Pending Phase
The API creates a pending transfer, reserving the funds in the ledger engine.
Evaluation Phase
A durable workflow executes the Rules Engine, evaluating all attached rules.
Finality
- Pass (enforce) or always (observe): The workflow captures the transfer (
confirm). - Fail (enforce only): The workflow releases the funds (
void) and records the rejection reason.