Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.dakota.xyz/llms.txt

Use this file to discover all available pages before exploring further.

A policy governs which transactions a wallet can authorize. Every wallet must have at least one policy attached before it can sign outbound transactions — Dakota’s policy engine default-denies any transaction on a wallet with zero attached policies ("transaction denied: No policies found for wallet", HTTP 403). Inbound deposits are independent of policies: an on-chain address can always receive crypto regardless of the wallet’s governance. Policies are versioned, mutable resources. You attach them to wallets, and a wallet can have multiple policies layered on top of one another. The policy engine evaluates all attached policies for every transaction and applies the result.

Anatomy of a Policy

A policy is three things:
  1. A signer group that governs the policy itself. Future mutations of the policy — adding rules, deleting it, updating limits — must be signed by members of this group via the endorsed-request flow. It can be the same group attached to the wallet, or a separate admin group for stricter compartmentalization.
  2. One or more rules. Each rule has a rule_type, an action (allow or deny), and a definition whose shape depends on the rule type.
  3. Metadata. A human-readable name and optional description.
{
  "id": "pol_2LfQm5KMnRvLFtRP7nJJug4zJEP",
  "client_id": "1NFHrqBHb3cTfLVkFSGmHZqdDPi",
  "signer_group_id": "grp_2LfPqT9VmQzKDvQP9rGHth3yHCN",
  "version": 1,
  "name": "Treasury default policy",
  "description": "Single-signature for transfers under $10k; two signatures otherwise",
  "rules": [
    { "rule_type": "approval_threshold", "action": "allow",
      "definition": { "threshold": 1 } },
    { "rule_type": "amount_threshold",   "action": "deny",
      "definition": { "amount": "10000", "currency": "USD" } }
  ],
  "created_at": 1640995200,
  "updated_at": 1640995200
}

Rule Types

There are three rule types. Each evaluates a different aspect of the transaction.

approval_threshold

Requires a minimum number of valid signatures from the policy’s signer group.
{
  "rule_type": "approval_threshold",
  "action": "allow",
  "definition": { "threshold": 2, "description": "Requires 2-of-N approval" }
}
  • threshold — minimum number of signers required.
  • The rule always applies to every transaction (it’s a count check, not a conditional).
  • If the threshold is met → emit the rule’s action (allow or deny). If not met → emit the inverted action.
The minimum permissive policy — “any single member can authorize anything” — is approval_threshold with threshold: 1 and action: allow.

amount_threshold

Sets transaction-value limits. Useful for tiered approval (small transfers single-sig, large transfers multi-sig) or hard caps.
{
  "rule_type": "amount_threshold",
  "action": "deny",
  "definition": { "amount": "10000", "currency": "USD" }
}
  • amount — decimal string, evaluated against the transaction’s amount in the policy’s currency.
  • The rule applies only when the transaction’s amount exceeds amount. Below threshold, the rule does not apply (does not contribute allow or deny).

address_list

Restricts which on-chain destinations a wallet can send to. Allow-list (only these) or deny-list (block these).
{
  "rule_type": "address_list",
  "action": "allow",
  "definition": {
    "addresses": [
      "0x742d35Cc6634C0532925a3b8D404fA40b5398Ad2",
      "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
    ]
  }
}
  • The rule applies only when the destination address is in the addresses list.
  • For an allow-list, set action: allow and pair with a default-deny in another rule (or rely on the policy engine’s default-deny on no-applicable-policy).
  • For a deny-list, set action: deny and the listed addresses are blocked outright.

Evaluation Order

For a given transaction, the policy engine:
  1. Loads every policy attached to the wallet. If zero policies, return deny immediately ("No policies found for wallet").
  2. For each policy, evaluates each rule in turn. A rule produces one of: applied + allow, applied + deny, or not-applicable.
  3. Aggregates per policy: if any rule denies → policy = deny. Else if any rule allows → policy = allow. Else (no rule applied) → policy = not-applicable.
  4. Aggregates across policies: if any policy denies → transaction = deny. Else if any policy allows → transaction = allow. Else (no applicable policy) → transaction = deny (default).
Translation: deny wins, allow loses, silence is deny. Layering policies is safe — adding more policies can only block transactions, never relax existing controls.

Lifecycle

OperationEndpointEndorsed?Signed by
Create policyPOST /policiesNoAPI key only
Add a rulePOST /policies/{policy_id}/rulesYesPolicy’s own signer group
Update a rulePATCH /policies/{policy_id}/rules/{rule_id}YesPolicy’s own signer group
Remove a ruleDELETE /policies/{policy_id}/rules/{rule_id}YesPolicy’s own signer group
Delete policyDELETE /policies/{policy_id}YesPolicy’s own signer group
Attach to walletPUT /policies/{policy_id}/wallets/{wallet_id}YesA signer group attached to the wallet
Detach from walletDELETE /policies/{policy_id}/wallets/{wallet_id}YesA signer group attached to the wallet
Policy creation itself is not endorsed — it’s a regular API request authenticated only by your API key. Once the policy exists, every subsequent mutation goes through the endorsed-request flow: canonicalize the intent per RFC 8785 JCS, sign with ECDSA P-256, encode as DER + base64.

Practical Examples

”Default permissive” — single signature, no restrictions

For sandbox testing or low-risk accounts:
{
  "name": "Default single-signature policy",
  "signer_group_id": "grp_admin",
  "rules": [
    { "rule_type": "approval_threshold", "action": "allow",
      "definition": { "threshold": 1 } }
  ]
}

“Tiered approval by amount” — single-sig under $10k, dual-sig above

{
  "name": "Tiered approval policy",
  "signer_group_id": "grp_admin",
  "rules": [
    { "rule_type": "approval_threshold", "action": "allow",
      "definition": { "threshold": 1 } },
    { "rule_type": "amount_threshold", "action": "deny",
      "definition": { "amount": "10000", "currency": "USD" } },
    { "rule_type": "approval_threshold", "action": "allow",
      "definition": { "threshold": 2 } }
  ]
}
The deny-wins logic blocks single-sig above $10k; the second approval_threshold rule lets dual-sig through.

”Sanctions deny-list” — block specific addresses outright

{
  "name": "OFAC blocklist",
  "signer_group_id": "grp_compliance",
  "rules": [
    { "rule_type": "address_list", "action": "deny",
      "definition": { "addresses": ["0xSanctionedAddress1", "0xSanctionedAddress2"] } }
  ]
}
Stack this alongside other policies — its deny will override any allow from sibling policies.