- Preparing and uploading our data.
- Creating our matching rules.
- Running our reconciliation process.
- Review the reconciliation results.
Prerequisites
Before starting, ensure you have:- A running Blnk server instance (e.g. at
http://localhost:5001). Version 0.10.0 or later.
Recording sample transactions
First, import the following sample transactions using the Bulk Transactions feature:curl -X POST "http://localhost:5001/transactions/bulk" \
-H "X-blnk-key: <api-key>" \
-H "Content-Type: application/json" \
-d '{
"atomic": true,
"inflight": false,
"run_async": false,
"transactions": [
{
"amount": 152.75,
"precision": 100,
"reference": "txn_001",
"description": "Invoice settlement",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": true,
"destination": "@WorldB"
},
{
"amount": 329.40,
"precision": 100,
"reference": "txn_002",
"description": "Refund processing",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": true,
"destination": "@WorldA"
},
{
"amount": 87.15,
"precision": 100,
"reference": "txn_003",
"description": "Service charge",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": true,
"destination": "@WorldB"
},
{
"amount": 413.60,
"precision": 100,
"reference": "txn_004",
"description": "Payment for goods",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": true,
"destination": "@WorldA"
},
{
"amount": 276.89,
"precision": 100,
"reference": "txn_005",
"description": "Subscription fee",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": true,
"destination": "@WorldB"
},
{
"amount": 199.99,
"precision": 100,
"reference": "txn_006",
"description": "Expense reimbursement",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": true,
"destination": "@WorldA"
},
{
"amount": 540.25,
"precision": 100,
"reference": "txn_007",
"description": "Project funding",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": true,
"destination": "@WorldB"
},
{
"amount": 135.70,
"precision": 100,
"reference": "txn_008",
"description": "Loan repayment",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": true,
"destination": "@WorldA"
},
{
"amount": 92.80,
"precision": 100,
"reference": "txn_009",
"description": "Freelance payment",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": true,
"destination": "@WorldB"
},
{
"amount": 480.55,
"precision": 100,
"reference": "txn_010",
"description": "Revenue share",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": true,
"destination": "@WorldA"
}
]
}'
const response = await blnk.Transactions.createBulk({
atomic: true,
inflight: false,
run_async: false,
transactions: [
{
amount: 152.75,
precision: 100,
reference: 'txn_001',
description: 'Invoice settlement',
currency: 'USD',
source: '@WorldA',
allow_overdraft: true,
destination: '@WorldB',
},
{
amount: 329.4,
precision: 100,
reference: 'txn_002',
description: 'Refund processing',
currency: 'USD',
source: '@WorldB',
allow_overdraft: true,
destination: '@WorldA',
},
{
amount: 87.15,
precision: 100,
reference: 'txn_003',
description: 'Service charge',
currency: 'USD',
source: '@WorldA',
allow_overdraft: true,
destination: '@WorldB',
},
{
amount: 413.6,
precision: 100,
reference: 'txn_004',
description: 'Payment for goods',
currency: 'USD',
source: '@WorldB',
allow_overdraft: true,
destination: '@WorldA',
},
{
amount: 276.89,
precision: 100,
reference: 'txn_005',
description: 'Subscription fee',
currency: 'USD',
source: '@WorldA',
allow_overdraft: true,
destination: '@WorldB',
},
{
amount: 199.99,
precision: 100,
reference: 'txn_006',
description: 'Expense reimbursement',
currency: 'USD',
source: '@WorldB',
allow_overdraft: true,
destination: '@WorldA',
},
{
amount: 540.25,
precision: 100,
reference: 'txn_007',
description: 'Project funding',
currency: 'USD',
source: '@WorldA',
allow_overdraft: true,
destination: '@WorldB',
},
{
amount: 135.7,
precision: 100,
reference: 'txn_008',
description: 'Loan repayment',
currency: 'USD',
source: '@WorldB',
allow_overdraft: true,
destination: '@WorldA',
},
{
amount: 92.8,
precision: 100,
reference: 'txn_009',
description: 'Freelance payment',
currency: 'USD',
source: '@WorldA',
allow_overdraft: true,
destination: '@WorldB',
},
{
amount: 480.55,
precision: 100,
reference: 'txn_010',
description: 'Revenue share',
currency: 'USD',
source: '@WorldB',
allow_overdraft: true,
destination: '@WorldA',
},
],
});
batch, resp, err := client.Transaction.CreateBulk(blnkgo.CreateBulkTransactionRequest{
Atomic: true,
Inflight: false,
RunAsync: false,
Transactions: []blnkgo.CreateTransactionRequest{
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 152.75,
Precision: 100,
Reference: "txn_001",
Description: "Invoice settlement",
Currency: "USD",
Source: "@WorldA",
Destination: "@WorldB",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 329.40,
Precision: 100,
Reference: "txn_002",
Description: "Refund processing",
Currency: "USD",
Source: "@WorldB",
Destination: "@WorldA",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 87.15,
Precision: 100,
Reference: "txn_003",
Description: "Service charge",
Currency: "USD",
Source: "@WorldA",
Destination: "@WorldB",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 413.60,
Precision: 100,
Reference: "txn_004",
Description: "Payment for goods",
Currency: "USD",
Source: "@WorldB",
Destination: "@WorldA",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 276.89,
Precision: 100,
Reference: "txn_005",
Description: "Subscription fee",
Currency: "USD",
Source: "@WorldA",
Destination: "@WorldB",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 199.99,
Precision: 100,
Reference: "txn_006",
Description: "Expense reimbursement",
Currency: "USD",
Source: "@WorldB",
Destination: "@WorldA",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 540.25,
Precision: 100,
Reference: "txn_007",
Description: "Project funding",
Currency: "USD",
Source: "@WorldA",
Destination: "@WorldB",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 135.70,
Precision: 100,
Reference: "txn_008",
Description: "Loan repayment",
Currency: "USD",
Source: "@WorldB",
Destination: "@WorldA",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 92.80,
Precision: 100,
Reference: "txn_009",
Description: "Freelance payment",
Currency: "USD",
Source: "@WorldA",
Destination: "@WorldB",
},
AllowOverdraft: true,
},
{
ParentTransaction: blnkgo.ParentTransaction{
Amount: 480.55,
Precision: 100,
Reference: "txn_010",
Description: "Revenue share",
Currency: "USD",
Source: "@WorldB",
Destination: "@WorldA",
},
AllowOverdraft: true,
},
},
})
response = blnk.transactions.create_bulk({
"atomic": True,
"inflight": False,
"run_async": False,
"transactions": [
{
"amount": 152.75,
"precision": 100,
"reference": "txn_001",
"description": "Invoice settlement",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": True,
"destination": "@WorldB",
},
{
"amount": 329.40,
"precision": 100,
"reference": "txn_002",
"description": "Refund processing",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": True,
"destination": "@WorldA",
},
{
"amount": 87.15,
"precision": 100,
"reference": "txn_003",
"description": "Service charge",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": True,
"destination": "@WorldB",
},
{
"amount": 413.60,
"precision": 100,
"reference": "txn_004",
"description": "Payment for goods",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": True,
"destination": "@WorldA",
},
{
"amount": 276.89,
"precision": 100,
"reference": "txn_005",
"description": "Subscription fee",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": True,
"destination": "@WorldB",
},
{
"amount": 199.99,
"precision": 100,
"reference": "txn_006",
"description": "Expense reimbursement",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": True,
"destination": "@WorldA",
},
{
"amount": 540.25,
"precision": 100,
"reference": "txn_007",
"description": "Project funding",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": True,
"destination": "@WorldB",
},
{
"amount": 135.70,
"precision": 100,
"reference": "txn_008",
"description": "Loan repayment",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": True,
"destination": "@WorldA",
},
{
"amount": 92.80,
"precision": 100,
"reference": "txn_009",
"description": "Freelance payment",
"currency": "USD",
"source": "@WorldA",
"allow_overdraft": True,
"destination": "@WorldB",
},
{
"amount": 480.55,
"precision": 100,
"reference": "txn_010",
"description": "Revenue share",
"currency": "USD",
"source": "@WorldB",
"allow_overdraft": True,
"destination": "@WorldA",
},
],
})
ApiResponse<JsonNode> response = blnk.transactions().createBulk(
BulkTransactions.create()
.atomic(true)
.inflight(false)
.runAsync(false)
.transactions(List.of(
CreateTransactions.create()
.amount(152.75)
.precision(100)
.reference("txn_001")
.description("Invoice settlement")
.currency("USD")
.source("@WorldA")
.allowOverdraft(true)
.destination("@WorldB"),
CreateTransactions.create()
.amount(329.40)
.precision(100)
.reference("txn_002")
.description("Refund processing")
.currency("USD")
.source("@WorldB")
.allowOverdraft(true)
.destination("@WorldA"),
CreateTransactions.create()
.amount(87.15)
.precision(100)
.reference("txn_003")
.description("Service charge")
.currency("USD")
.source("@WorldA")
.allowOverdraft(true)
.destination("@WorldB"),
CreateTransactions.create()
.amount(413.60)
.precision(100)
.reference("txn_004")
.description("Payment for goods")
.currency("USD")
.source("@WorldB")
.allowOverdraft(true)
.destination("@WorldA"),
CreateTransactions.create()
.amount(276.89)
.precision(100)
.reference("txn_005")
.description("Subscription fee")
.currency("USD")
.source("@WorldA")
.allowOverdraft(true)
.destination("@WorldB"),
CreateTransactions.create()
.amount(199.99)
.precision(100)
.reference("txn_006")
.description("Expense reimbursement")
.currency("USD")
.source("@WorldB")
.allowOverdraft(true)
.destination("@WorldA"),
CreateTransactions.create()
.amount(540.25)
.precision(100)
.reference("txn_007")
.description("Project funding")
.currency("USD")
.source("@WorldA")
.allowOverdraft(true)
.destination("@WorldB"),
CreateTransactions.create()
.amount(135.70)
.precision(100)
.reference("txn_008")
.description("Loan repayment")
.currency("USD")
.source("@WorldB")
.allowOverdraft(true)
.destination("@WorldA"),
CreateTransactions.create()
.amount(92.80)
.precision(100)
.reference("txn_009")
.description("Freelance payment")
.currency("USD")
.source("@WorldA")
.allowOverdraft(true)
.destination("@WorldB"),
CreateTransactions.create()
.amount(480.55)
.precision(100)
.reference("txn_010")
.description("Revenue share")
.currency("USD")
.source("@WorldB")
.allowOverdraft(true)
.destination("@WorldA")
))
);
Response
{
"batch_id": "bulk_4192d961-5b0e-46ca-bf2f-9386763057f8",
"status": "applied",
"transaction_count": 10
}
Preparing the external data
Next, download the sample CSV file provided below. This file will act as our external data source and has already been formatted to meet Blnk’s specifications. See External Data Preparation for more details.External data source
Sample CSV for reconciliation practice.
Uploading the data
Upload your CSV file with the Upload Data endpoint:curl --location 'https://YOUR_BLNK_INSTANCE_URL/reconciliation/upload' \
--form 'file=@recon-example-external-source.csv' \
--form 'source=Stripe'
const response = await blnk.Reconciliation.upload(
'recon-example-external-source.csv',
'Stripe',
);
file, _ := os.Open("recon-example-external-source.csv")
result, resp, err := client.Reconciliation.Upload("Stripe", file, file.Name())
response = blnk.reconciliation.upload("recon-example-external-source.csv", "Stripe")
ApiResponse<JsonNode> response = blnk.reconciliation().upload(
"recon-example-external-source.csv",
"Stripe"
);
Response
{
"record_count": 10,
"source": "Stripe",
"upload_id": "upload_0cd4a87d-544c-4b7e-9232-f36e79631c32"
}
Save the
upload_id. You’ll need it to start the reconciliation.Setting our matching rules
We’ll define rules to match internal and external transactions. For this example, we want:- Amount Match: Exact amount match
- Currency Match: Exact matches for both fields.
- Reference Match: Exact matches for both references
curl -X POST "http://localhost:5001/reconciliation/matching-rules" \
-H "X-blnk-key: <api-key>" \
-H "Content-Type: application/json" \
-d '{
"name": "Basic matcher",
"description": "Matches transactions by amount and reference",
"criteria": [
{
"field": "amount",
"operator": "equals"
},
{
"field": "currency",
"operator": "equals"
},
{
"field": "reference",
"operator": "equals"
}
]
}'
const response = await blnk.Reconciliation.createMatchingRule({
name: 'Basic matcher',
description: 'Matches transactions by amount and reference',
criteria: [
{
field: 'amount',
operator: 'equals',
},
{
field: 'currency',
operator: 'equals',
},
{
field: 'reference',
operator: 'equals',
},
],
});
rule, resp, err := client.Reconciliation.CreateMatchingRule(blnkgo.Matcher{
Name: "Basic matcher",
Description: "Matches transactions by amount and reference",
Criteria: []blnkgo.Criteria{
{
Field: blnkgo.CriteriaFieldAmount,
Operator: blnkgo.ReconciliationOperatorEquals,
},
{
Field: blnkgo.CriteriaFieldCurrency,
Operator: blnkgo.ReconciliationOperatorEquals,
},
{
Field: blnkgo.CriteriaFieldReference,
Operator: blnkgo.ReconciliationOperatorEquals,
},
},
})
response = blnk.reconciliation.create_matching_rule({
"name": "Basic matcher",
"description": "Matches transactions by amount and reference",
"criteria": [
{
"field": "amount",
"operator": "equals",
},
{
"field": "currency",
"operator": "equals",
},
{
"field": "reference",
"operator": "equals",
},
],
})
ApiResponse<JsonNode> response = blnk.reconciliation().createMatchingRule(
Matcher.create()
.name("Basic matcher")
.description("Matches transactions by amount and reference")
.criteria(List.of(
Criteria.create()
.field("amount")
.operator("equals"),
Criteria.create()
.field("currency")
.operator("equals"),
Criteria.create()
.field("reference")
.operator("equals")
))
);
Response
{
"rule_id": "rule_3d5ae6d4-6722-40c5-ac4b-0bd66ab7d363",
"created_at": "2025-03-17T08:31:24.97610651Z",
"updated_at": "2025-03-17T08:31:24.976106635Z",
"name": "Basic matcher",
"description": "Matches transactions by amount and reference",
"criteria": [
{
"field": "amount",
"operator": "equals",
"value": "",
"pattern": "",
"allowable_drift": 0
},
{
"field": "currency",
"operator": "equals",
"value": "",
"pattern": "",
"allowable_drift": 0
},
{
"field": "reference",
"operator": "equals",
"value": "",
"pattern": "",
"allowable_drift": 0
}
]
}
Save the
rule_id for the reconciliation step.Run reconciliation
With the external data uploaded and our matching rules configured, we’re ready to run our first reconciliation. In this example, we’ll use the Batch Reconciliation option and apply aone_to_one strategy to ensure precise matching.
See Reconciliation Strategies for details.
Send this request to start the reconciliation:
curl -X POST "http://localhost:5001/reconciliation/start" \
-H "X-blnk-key: <api-key>" \
-H "Content-Type: application/json" \
-d '{
"upload_id": "upload_0cd4a87d-544c-4b7e-9232-f36e79631c32",
"strategy": "one_to_one",
"matching_rule_ids": [
"rule_3d5ae6d4-6722-40c5-ac4b-0bd66ab7d363"
]
}'
const response = await blnk.Reconciliation.run({
upload_id: 'upload_0cd4a87d-544c-4b7e-9232-f36e79631c32',
strategy: 'one_to_one',
matching_rule_ids: [
'rule_3d5ae6d4-6722-40c5-ac4b-0bd66ab7d363',
],
});
result, resp, err := client.Reconciliation.Run(blnkgo.RunReconData{
UploadID: "upload_0cd4a87d-544c-4b7e-9232-f36e79631c32",
Strategy: blnkgo.ReconciliationStrategyOneToOne,
MatchingRuleIDs: []string{
"rule_3d5ae6d4-6722-40c5-ac4b-0bd66ab7d363",
},
})
response = blnk.reconciliation.run({
"upload_id": "upload_0cd4a87d-544c-4b7e-9232-f36e79631c32",
"strategy": "one_to_one",
"matching_rule_ids": [
"rule_3d5ae6d4-6722-40c5-ac4b-0bd66ab7d363",
],
})
ApiResponse<JsonNode> response = blnk.reconciliation().run(
RunReconData.create()
.uploadId("upload_0cd4a87d-544c-4b7e-9232-f36e79631c32")
.strategy("one_to_one")
.matchingRuleIds(List.of(
"rule_3d5ae6d4-6722-40c5-ac4b-0bd66ab7d363"
))
);
Response
{
"reconciliation_id": "recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58"
}
View reconciliation results
Finally, you can view the status of your reconciliation with the following endpoint:curl -X GET "https://YOUR_BLNK_INSTANCE_URL/reconciliation/{reconciliation_id}" \
-H "X-blnk-key: <api-key>"
const response = await blnk.Reconciliation.get(
'{reconciliation_id}',
);
recon, resp, err := client.Reconciliation.Get(
"{reconciliation_id}",
)
response = blnk.reconciliation.get(
"{reconciliation_id}",
)
ApiResponse<JsonNode> response = blnk.reconciliation().get(
"{reconciliation_id}"
);
Response
{
"reconciliation_id": "recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58",
"upload_id": "upload_0cd4a87d-544c-4b7e-9232-f36e79631c32",
"status": "completed",
"matched_transactions": 2,
"unmatched_transactions": 8,
"is_dry_run": false,
"started_at": "2025-03-17T08:32:24.005476Z",
"completed_at": "2025-03-17T08:32:24.062136Z"
}
| Field | Description |
|---|---|
matched_transactions | Number of external records matched. |
unmatched_transactions | Number of external records not matched. |
Error handling
-
401 Unauthorized: Ensure your API key is correct and included in the
Authorizationheader. - 400 Bad Request: Check your request body or file format. For uploads, ensure the CSV matches Blnk’s specifications.
-
404 Not Found: Verify the endpoint URL (e.g.,
/reconciliation/uploadfor uploads,/reconciliation/startfor reconciliation).