Transaction Approval Webhook
BitPowr Transaction Approval Webhook Guide
Overview
The BitPowr Transaction Approval Webhook allows you to programmatically approve or reject transactions before they are processed. This powerful feature enables custom approval logic, compliance checks, fraud detection, and transaction validation based on your business requirements.
How to Setup
- Navigate to the account Policy Page
- Find Transaction Approval Webhook URL section
- Enter your webhook URL (e.g.,
https://yourdomain.com/webhook/transaction-approval
) - Check Enable checkbox
- Click Save changes
How Transaction Approval Works
Operation Flows
When a new outgoing Transaction is created
↓
BitPowr sends webhook payload to your URL
↓
Your system processes the payload
↓
Return HTTP 200 (Approve) or 400+ (Reject)
↓
Transaction moves to APPROVED or REJECTED status
Response Handling
- HTTP 200: Transaction approved, processing continues
- HTTP 400+: Transaction rejected, processing stops
- Any Error: Automatic rejection (timeout, network error, etc.)
Webhook Payload Structure
When a transaction requires approval, BitPowr sends a POST request to your webhook URL with the following payload:
{
"transactionId": "6680c116-c803-4e46-aa99-2f261cd216ac",
"walletId": "e3455-15b5-46ad-84de-34555",
"assetType": "USDC_ETH",
"amount": "1000.00",
"to": [
{address: "0x742d35Cc6644C0532925a3b8C17DAb6F2c8f1234", value: 1000}
],
"from": [],
"fromUtxo": [],
"description": "API-VALIDATED: Monthly vendor payment #12345",
"category": "PAYMENT",
"transactionType": "TRANSFER",
"source": "API",
"ipAddress": "192.168.1.100",
"userAgent": "MyApp/1.0 (https://myapp.com)",
"timestamp": "2025-01-15T10:30:00Z",
"metadata": {
"customerId": "customer-premium-789",
"subAccountId": "vendor-payments",
"memo": "Invoice #INV-2025-001",
"idempotencyKey": "vendor-payment-12345-2025-01"
}
}
Payload Field Descriptions
Field | Type | Description |
---|---|---|
transactionId | string | Unique BitPowr transaction identifier. In this case the UID |
walletId | string | Wallet ID initiating the transaction |
assetType | string | Type of asset being transferred |
to | array | Destination tos. Array of (address, value) |
from | array | List of addresses to spend from |
fromUtxo | array | List of UTXO to spend from |
description | string | Transaction description |
category | string | Transaction category (withdrawal, gas_station, etc.) |
transactionType | string | Type of transaction (TRANSFER, CONTRACT_INTERACTION, etc.) |
source | string | Source of transaction (API, Dashboard, System) |
ipAddress | string | IP address of the request origin |
userAgent | string | User agent string from the request |
timestamp | string | ISO timestamp of transaction creation |
metadata | object | Additional transaction metadata |
Approval Response Formats
Approve Transaction
HTTP/1.1 200 OK
Content-Type: application/json
{
"approved": true,
"message": "Transaction approved - matches database record",
"approvalCode": "AUTO-APPROVED-001",
"approvedBy": "system-validator",
"approvedAt": "2025-01-15T10:30:05Z"
}
Reject Transaction
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"approved": false,
"message": "Transaction rejected - not found in database",
"rejectionCode": "DB-VALIDATION-FAILED",
"rejectedBy": "system-validator",
"rejectedAt": "2025-01-15T10:30:05Z"
}
Implementation Examples
1. Database Validation Webhook
This example checks if API transactions exist in your database before approval.
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook/transaction-approval', async (req, res) => {
try {
const {
transactionId,
source,
description,
amount,
address,
assetType,
metadata
} = req.body;
// Only validate API transactions
if (source !== 'API') {
return res.status(200).json({
approved: true,
message: 'Non-API transaction auto-approved',
approvalCode: 'AUTO-APPROVED-NON-API'
});
}
// Check if transaction exists in database
const dbTransaction = await findTransactionInDatabase({
idempotencyKey: metadata.idempotencyKey,
amount: amount,
address: address,
assetType: assetType
});
if (dbTransaction) {
// Transaction found - approve
await updateTransactionStatus(dbTransaction.id, 'APPROVED');
return res.status(200).json({
approved: true,
message: 'Transaction approved - found in database',
approvalCode: 'DB-VALIDATION-PASSED',
approvedBy: 'automated-system',
dbTransactionId: dbTransaction.id
});
} else {
// Transaction not found - reject
await logRejection(transactionId, 'DB-VALIDATION-FAILED');
return res.status(400).json({
approved: false,
message: 'Transaction rejected - not found in database',
rejectionCode: 'DB-VALIDATION-FAILED',
rejectedBy: 'automated-system'
});
}
} catch (error) {
console.error('Webhook error:', error);
// Return rejection on any error
return res.status(500).json({
approved: false,
message: 'Transaction rejected - system error',
rejectionCode: 'SYSTEM-ERROR',
error: error.message
});
}
});
const findTransactionInDatabase = async (criteria) => {
// Your database query logic here
return await db.transactions.findOne({
where: {
idempotencyKey: criteria.idempotencyKey,
amount: criteria.amount,
status: 'PENDING_APPROVAL'
}
});
};
2. Description-Based Validation
This example uses specific text in the description to determine which transactions to validate.
app.post('/webhook/transaction-approval', async (req, res) => {
try {
const {
transactionId,
source,
description,
amount,
address,
assetType
} = req.body;
// Check if description contains validation marker
const requiresValidation = description.includes('API-VALIDATED:');
if (source === 'API' && requiresValidation) {
// Extract order ID from description
const orderIdMatch = description.match(/API-VALIDATED:.*#(\w+)/);
const orderId = orderIdMatch ? orderIdMatch[1] : null;
if (!orderId) {
return res.status(400).json({
approved: false,
message: 'Invalid description format - missing order ID',
rejectionCode: 'INVALID-FORMAT'
});
}
// Validate order exists
const order = await findOrderById(orderId);
if (!order) {
return res.status(400).json({
approved: false,
message: `Order ${orderId} not found`,
rejectionCode: 'ORDER-NOT-FOUND'
});
}
// Validate order details match transaction
if (order.amount !== parseFloat(amount) || order.address !== address) {
return res.status(400).json({
approved: false,
message: 'Order details do not match transaction',
rejectionCode: 'DETAILS-MISMATCH'
});
}
// All validations passed
return res.status(200).json({
approved: true,
message: `Transaction approved for order ${orderId}`,
approvalCode: 'ORDER-VALIDATED',
orderId: orderId
});
}
// Non-validated transactions auto-approve
return res.status(200).json({
approved: true,
message: 'Transaction auto-approved - no validation required',
approvalCode: 'AUTO-APPROVED'
});
} catch (error) {
return res.status(500).json({
approved: false,
message: 'System error during validation',
rejectionCode: 'SYSTEM-ERROR'
});
}
});
3. Multi-Factor Approval System
This example implements different approval rules based on amount and source.
app.post('/webhook/transaction-approval', async (req, res) => {
try {
const {
transactionId,
source,
amount,
assetType,
ipAddress,
userAgent,
metadata
} = req.body;
const transactionAmount = parseFloat(amount);
// High-value transaction checks
if (transactionAmount > 10000) {
const manualApproval = await checkManualApproval(transactionId);
if (!manualApproval) {
return res.status(400).json({
approved: false,
message: 'High-value transaction requires manual approval',
rejectionCode: 'MANUAL-APPROVAL-REQUIRED'
});
}
}
// IP whitelist check for API transactions
if (source === 'API') {
const isWhitelisted = await checkIPWhitelist(ipAddress);
if (!isWhitelisted) {
return res.status(400).json({
approved: false,
message: 'IP address not whitelisted for API transactions',
rejectionCode: 'IP-NOT-WHITELISTED'
});
}
}
// Rate limiting check
const recentTransactions = await getRecentTransactions(
metadata.customerId,
'1hour'
);
if (recentTransactions.length > 10) {
return res.status(400).json({
approved: false,
message: 'Rate limit exceeded - too many transactions',
rejectionCode: 'RATE-LIMIT-EXCEEDED'
});
}
// All checks passed
return res.status(200).json({
approved: true,
message: 'Transaction approved - all checks passed',
approvalCode: 'MULTI-FACTOR-APPROVED',
checks: {
amount: transactionAmount <= 10000 ? 'auto' : 'manual',
ipWhitelist: 'passed',
rateLimit: 'passed'
}
});
} catch (error) {
return res.status(500).json({
approved: false,
message: 'Error during approval process',
rejectionCode: 'SYSTEM-ERROR'
});
}
});
Use Cases
1. Transaction Database Validation
Scenario: Ensure all API transactions exist in your system before processing.
Implementation:
- Check
source === 'API'
- Lookup transaction by
idempotencyKey
or custom identifier - Approve if found, reject if not found
2. Order Validation
Scenario: Validate transactions against order records.
Implementation:
- Extract order ID from description
- Validate order exists and matches transaction details
- Check order status is ready for payment
3. Fraud Detection
Scenario: Detect suspicious transactions based on patterns.
Implementation:
- Check IP address against whitelist/blacklist
- Analyze transaction frequency and amounts
- Validate user agent patterns
4. Compliance Checking
Scenario: Ensure transactions meet regulatory requirements.
Implementation:
- Check transaction amounts against limits
- Validate recipient addresses against sanctions lists
- Verify customer KYC status
5. Budget Control
Scenario: Enforce spending limits by customer or category.
Implementation:
- Track spending by customer ID
- Check against budget limits
- Reject transactions exceeding limits
6. Time-Based Restrictions
Scenario: Only allow transactions during business hours.
Implementation:
- Check transaction timestamp
- Validate against business hours
- Reject transactions outside allowed times
Webhook Requirements
- HTTPS Required: Webhook URL must use HTTPS in production
- Response Time: Must respond within 30 seconds
- Status Codes: 200 for approval, 400+ for rejection
- Content-Type: application/json recommended
This comprehensive webhook system gives you complete control over transaction approval while maintaining security and reliability.
Updated about 15 hours ago