This guide covers how to manage policies and handle approval workflows using the Dfns API and SDK.
Prerequisites
Service account or authenticated user with policy management permissions
Dfns SDK installed and configured
Understanding of policies
Creating a policy
Create a policy that requires approval for large transactions:
import { DfnsApiClient } from '@dfns/sdk'
const dfns = new DfnsApiClient ({
baseUrl: 'https://api.dfns.io' ,
// Your signer configuration
})
const policy = await dfns . policies . createPolicy ({
body: {
name: 'Large Transaction Approval' ,
activityKind: 'Wallets:Sign' ,
rule: {
kind: 'TransactionAmountLimit' ,
configuration: {
limit: '100000' ,
currency: 'USD'
}
},
action: {
kind: 'RequestApproval' ,
approvalGroups: [
{
name: 'Treasury' ,
quorum: 2 ,
approvers: {
userId: {
in: [ 'us-xxx-1' , 'us-xxx-2' , 'us-xxx-3' ]
}
},
serviceAccountsCanApprove: false , // set to true to allow service accounts to vote (requires staff activation)
}
],
autoRejectTimeout: 86400 // 24 hours
},
status: 'Active'
}
})
console . log ( 'Policy ID:' , policy . id )
Policy rule types
Transaction amount limit
Trigger when a single transaction exceeds a threshold:
rule : {
kind : 'TransactionAmountLimit' ,
configuration : {
limit : '100000' ,
currency : 'USD'
}
}
Transaction amount velocity
Trigger when total transaction value exceeds a limit within a time window:
rule : {
kind : 'TransactionAmountVelocity' ,
configuration : {
limit : '500000' ,
currency : 'USD' ,
timeWindow : 86400 // 24 hours in seconds
}
}
Transaction count velocity
Trigger when transaction count exceeds a limit within a time window:
rule : {
kind : 'TransactionCountVelocity' ,
configuration : {
limit : 100 ,
timeWindow : 86400
}
}
Recipient whitelist
Trigger when recipient is NOT in the whitelist:
rule : {
kind : 'TransactionRecipientWhitelist' ,
configuration : {
addresses : [
'0x...address1' ,
'0x...address2'
]
}
}
Always trigger
Trigger on all matching activities:
rule : {
kind : 'AlwaysTrigger' ,
configuration : {}
}
Policy actions
Request approval
Require human approval before proceeding:
action : {
kind : 'RequestApproval' ,
approvalGroups : [
{
name: 'Finance' ,
quorum: 1 ,
approvers: {
userId: { in: [ 'us-xxx-1' , 'us-xxx-2' ] }
}
}
],
autoRejectTimeout : 86400
}
Block
Block the activity entirely:
action : {
kind : 'Block'
}
Policy filters
Apply policies to specific wallets using tags:
const policy = await dfns . policies . createPolicy ({
body: {
name: 'Treasury Policy' ,
activityKind: 'Wallets:Sign' ,
rule: {
kind: 'TransactionAmountLimit' ,
configuration: { limit: '50000' , currency: 'USD' }
},
action: {
kind: 'RequestApproval' ,
approvalGroups: [ /* ... */ ]
},
filters: {
walletTags: {
hasAny: [ 'treasury' ]
}
},
status: 'Active'
}
})
Listing policies
Get all policies:
const policies = await dfns . policies . listPolicies ()
for ( const policy of policies . items ) {
console . log ( ` ${ policy . name } : ${ policy . status } ` )
}
Updating a policy
Update an existing policy:
await dfns . policies . updatePolicy ({
policyId: 'pl-xxx-xxx' ,
body: {
name: 'Updated Policy Name' ,
status: 'Active'
}
})
Deleting a policy
Delete a policy:
await dfns . policies . deletePolicy ({
policyId: 'pl-xxx-xxx'
})
Working with approvals
Listing pending approvals
Get approvals awaiting action:
const approvals = await dfns . policyApprovals . listApprovals ({
query: {
status: 'Pending'
}
})
for ( const approval of approvals . items ) {
console . log ( `Approval ${ approval . id } : ${ approval . activity . kind } ` )
}
Getting approval details
Get details for a specific approval:
const approval = await dfns . policyApprovals . getApproval ({
approvalId: 'pa-xxx-xxx'
})
console . log ( 'Activity:' , approval . activity )
console . log ( 'Triggered policies:' , approval . triggeredPolicies )
console . log ( 'Decisions so far:' , approval . decisions )
Submitting an approval decision
Approve or reject a pending approval:
// Approve
await dfns . policyApprovals . createApprovalDecision ({
approvalId: 'pa-xxx-xxx' ,
body: {
value: 'Approved' ,
reason: 'Verified transaction details with finance team'
}
})
// Reject
await dfns . policyApprovals . createApprovalDecision ({
approvalId: 'pa-xxx-xxx' ,
body: {
value: 'Rejected' ,
reason: 'Recipient address not verified'
}
})
The user or service account submitting the decision must be listed in one of the approval groups defined in the policy. The initiator cannot approve their own transactions by default. Service accounts can submit decisions when serviceAccountsCanApprove is enabled on the group.
Webhook notifications
Subscribe to policy events:
await dfns . webhooks . createWebhook ({
body: {
url: 'https://your-app.com/webhooks/dfns' ,
events: [
'policy.approval.pending' ,
'policy.approval.resolved' ,
'policy.triggered'
],
status: 'Enabled'
}
})
Handle approval events:
app . post ( '/webhooks/dfns' , async ( req , res ) => {
const event = req . body
switch ( event . kind ) {
case 'policy.approval.pending' :
// Notify approvers
await notifyApprovers ( event . data )
break
case 'policy.approval.resolved' :
// Approval completed (approved or rejected)
await handleApprovalResolved ( event . data )
break
}
res . status ( 200 ). send ( 'OK' )
})
Example: Multi-tier approval
Create a policy with multiple approval tiers:
// Tier 1: Operations approval for $10k+
await dfns . policies . createPolicy ({
body: {
name: 'Operations Approval' ,
activityKind: 'Wallets:Sign' ,
rule: {
kind: 'TransactionAmountLimit' ,
configuration: { limit: '10000' , currency: 'USD' }
},
action: {
kind: 'RequestApproval' ,
approvalGroups: [{
name: 'Operations' ,
quorum: 1 ,
approvers: { userId: { in: [ 'ops-user-1' , 'ops-user-2' ] } }
}]
},
status: 'Active'
}
})
// Tier 2: Finance approval for $100k+
await dfns . policies . createPolicy ({
body: {
name: 'Finance Approval' ,
activityKind: 'Wallets:Sign' ,
rule: {
kind: 'TransactionAmountLimit' ,
configuration: { limit: '100000' , currency: 'USD' }
},
action: {
kind: 'RequestApproval' ,
approvalGroups: [{
name: 'Finance' ,
quorum: 2 ,
approvers: { userId: { in: [ 'finance-1' , 'finance-2' , 'finance-3' ] } }
}]
},
status: 'Active'
}
})
Automated approvals with service accounts
Service accounts can participate in approval groups, enabling automated approval workflows. For example, a compliance service that checks transactions against internal rules before approving.
This feature requires activation by Dfns staff on your organization. Contact support to enable it.
Setting up the policy
Create a policy with a service account in an approval group. The service account must be explicitly listed in approvers:
const policy = await dfns . policies . createPolicy ({
body: {
name: 'Automated Compliance Check' ,
activityKind: 'Wallets:Sign' ,
rule: {
kind: 'TransactionAmountLimit' ,
configuration: {
limit: '10000' ,
currency: 'USD'
}
},
action: {
kind: 'RequestApproval' ,
approvalGroups: [
{
name: 'Compliance' ,
quorum: 1 ,
approvers: {
userId: {
in: [ 'us-sa-xxx' , 'us-xxx-1' , 'us-xxx-2' ] // service account + human fallbacks
}
},
serviceAccountsCanApprove: true ,
}
],
autoRejectTimeout: 86400
},
status: 'Active'
}
})
The group has a quorum of 1 and includes both the service account and human approvers. The service account evaluates the transaction first. If it approves, the quorum is met and the transaction proceeds. If the service account can’t approve, it notifies a human to review and approve instead. Either way, every approval decision is recorded with full accountability.
A service account rejection denies the entire approval. Design your automation to abstain rather than reject when unsure. Notify a human approver and let them make the call within Dfns. This preserves the audit trail and non-repudiation.
Processing approvals as a service account
Use webhooks to react to pending approvals in real time. When a policy.approval.pending event arrives, the service account evaluates the transaction. If it passes, the service account approves. Otherwise, it notifies a human to review:
app . post ( '/webhooks/dfns' , async ( req , res ) => {
const event = req . body
if ( event . kind !== 'policy.approval.pending' ) {
return res . status ( 200 ). send ( 'OK' )
}
const approval = await dfns . policyApprovals . getApproval ({
approvalId: event . data . approvalId ,
})
// Run your approval logic (e.g. sanctions screening, internal limits)
const decision = await evaluateTransaction ( approval . activity )
if ( decision . approved ) {
await dfns . policyApprovals . createApprovalDecision ({
approvalId: approval . id ,
body: {
value: 'Approved' ,
reason: decision . reason ,
},
})
} else {
// Don't reject: let a human approver review instead
await notifyHumanApprover ( approval , decision . reason )
}
res . status ( 200 ). send ( 'OK' )
})
Policies API Complete API reference
Approvals API Approval management API
Policies concept Understanding policies
Webhooks Event notifications