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
| Operation | Endpoint | Purpose |
|---|---|---|
| Get controllers | POST /v2/rest/getStateControllers | List controllers and current entries for a key |
| Create controller | POST /v2/rest/createStateController | Create one controller for future policy state binding |
| Delete controller | POST /v2/rest/deleteStateController | Remove 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": "Daily rolling spend limit",
"method": "sum",
"window_config": {
"type": "rolling",
"seconds": 86400
},
"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,
controllerswill be empty. - Right after creating a controller (before sign traffic),
entriesis typically empty. referenced_byisnulluntil the controller is bound by a policy condition usingstate.id.
2) Create state controller
Request
POST /v2/rest/createStateController
{
"payload": {
"key_id": "alice_key_id",
"description": "Daily rolling spend limit",
"method": "sum",
"window": {
"type": "rolling",
"seconds": 86400
},
"partition_by": [
{
"transaction_type": "erc20",
"transaction_attr": "to"
}
]
},
"userSignatures": { ... }
}
Payload fields
| Field | Type | Required | Description |
|---|---|---|---|
key_id | string | Yes | Key the controller belongs to |
description | string | No | Human-readable label |
method | "sum" | "count" | Yes | Aggregation method |
window | object | Yes | Window config: rolling or fixed |
partition_by | array | Yes | Non-empty partition field list |
window options:
- Rolling:
{ "type": "rolling", "seconds": <number> } - Fixed:
{ "type": "fixed", "interval": "day" | "week" | "month" }
interval behavior:
dayresets at00:00:00 UTCeach calendar day.weekresets every Monday at00:00:00 UTC, withwindow_startanchored to the Monday of the current week once a partition is evaluated.monthresets at the first day of the next month; the entry recordswindow_startas the first day of the current month at00:00 UTCwhen it is evaluated.
Response
{
"id": "ctrl_123",
"key_id": "alice_key_id",
"description": "Daily rolling spend limit",
"method": "sum",
"window_config": {
"type": "rolling",
"seconds": 86400
},
"partition_by": [
{
"transaction_type": "erc20",
"transaction_attr": "to"
}
],
"referenced_by": null
}
Notes
- Each key can have up to
20controllers. - 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 returnsdeny.
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 thestate.idreference from the policy.
Integration flow
- Create controller (
createStateController). - Insert returned
idinto policy condition asstate.id. - Update policy.
- Sign traffic to populate entries over time.
- Query controllers (
getStateControllers) to inspect entries.