Skip to main content

State Controller Management

Reference documentation for the State Controller Management API endpoints.

If you are new to the feature, start with Stateful Policy Onboarding: Duo, Trio, Silent Network.

Endpoint summary

OperationEndpointPurpose
Get controllersPOST /v2/rest/getStateControllersList controllers and current entries for a key
Create controllerPOST /v2/rest/createStateControllerCreate one controller for future policy state binding
Delete controllerPOST /v2/rest/deleteStateControllerRemove one controller and its associated state

Common request envelope

All endpoints use this envelope:

{
"payload": { "...": "..." },
"userSignatures": { ... }
}

userSignatures follows the same signed-auth pattern as policy management APIs.

1) Get state controllers

Request

POST /v2/rest/getStateControllers

{
"payload": {
"key_id": "alice_key_id"
},
"userSignatures": { ... }
}

Response

{
"controllers": [
{
"id": "ctrl_123",
"key_id": "alice_key_id",
"description": "Rolling 24h spend limit",
"method": "sum",
"window_config": {
"type": "rolling",
"interval": "day",
"count": 1
},
"partition_by": [
{
"transaction_type": "erc20",
"transaction_attr": "to"
}
],
"referenced_by": "0xabc...",
"entries": [
{
"controller_id": "ctrl_123",
"partition_key": "0x...",
"value": "650"
}
]
}
]
}

Notes

  • For a new key, controllers will be empty.
  • Right after creating a controller (before sign traffic), entries is typically empty.
  • referenced_by is null until the controller is bound by a policy condition using state.id.

2) Create state controller

Request

POST /v2/rest/createStateController

{
"payload": {
"key_id": "alice_key_id",
"description": "Rolling 24h spend limit",
"method": "sum",
"window": "{\"type\":\"rolling\",\"interval\":\"day\",\"count\":1}",
"partition_by": "[{\"transaction_type\":\"erc20\",\"transaction_attr\":\"to\"}]"
},
"userSignatures": { ... }
}

In the request, window and partition_by are JSON strings (they are part of the signed payload). In the response they are returned as parsed JSON objects (window_config, partition_by).

Payload fields

FieldTypeRequiredDescription
key_idstringYesKey the controller belongs to
descriptionstringNoHuman-readable label
method"sum" | "count"YesAggregation method
windowstring (JSON)YesSerialized rolling window config
partition_bystring (JSON)YesSerialized non-empty partition field list

See State Controller and Rolling Window Mechanics for details on how to use window and partition_by.

Response

{
"id": "ctrl_123",
"key_id": "alice_key_id",
"description": "Rolling 24h spend limit",
"method": "sum",
"window_config": {
"type": "rolling",
"interval": "day",
"count": 1
},
"partition_by": [
{
"transaction_type": "erc20",
"transaction_attr": "to"
}
],
"referenced_by": null
}

Notes

  • Each key can have up to 20 controllers.
  • Create controller first, then update policy to reference state.id.
  • Changing semantics (method, window, partition_by, or bound condition fields) without recreating the controller leaves the linked condition invalid and every evaluation returns deny.

3) Delete state controller

Request

POST /v2/rest/deleteStateController

{
"payload": {
"key_id": "alice_key_id",
"controller_id": "ctrl_123"
},
"userSignatures": { ... }
}

Response

{
"status": "success"
}

Notes

  • Deleting a controller removes its associated state.
  • If policy still references a deleted state.id, that stateful condition will fail evaluation. To restore the condition, either create a new controller or remove the state.id reference from the policy.

Integration flow

  1. Create controller (createStateController).
  2. Insert returned id into policy condition as state.id.
  3. Update policy.
  4. Sign traffic to populate entries over time.
  5. Query controllers (getStateControllers) to inspect entries.