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']
}
}
}
],
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 submitting the decision must be in one of the approval groups defined in the policy. The initiator cannot approve their own transactions by default.
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'
}
})