Reconciliation

Practice example

Run a one-to-one reconciliation with sample transactions and a sample file.

Record ten ledger transactions, upload a sample file, create one matching rule, and read the result.

This walkthrough uses one-to-one, so each external row is compared with one ledger transaction.

You need a Core instance at http://localhost:5001.

Record sample transactions

Post the ten sample transactions with Bulk transactions.

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, _, _ := 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")
        ))
);
{
  "batch_id": "bulk_4192d961-5b0e-46ca-bf2f-9386763057f8",
  "status": "applied",
  "transaction_count": 10
}

Prepare the external file

Download the sample CSV. The rows already use the fields on External data.

Upload the file

Call Upload external records. Keep the upload_id from the response.

curl -X POST "http://localhost:5001/reconciliation/upload" \
  -H "X-blnk-key: <api-key>" \
  -F "file=@recon-example-external-source.csv" \
  -F "source=Stripe"
const response = await blnk.Reconciliation.upload(
  'recon-example-external-source.csv',
  'Stripe',
);
file, _ := os.Open("recon-example-external-source.csv")
result, _, _ := 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"
);
{
  "upload_id": "upload_0cd4a87d-544c-4b7e-9232-f36e79631c32",
  "record_count": 10,
  "source": "Stripe"
}

Create a matching rule

This rule matches when amount, currency, and reference are all equal. Every criterion has to match.

Call Create matching rule and keep the rule_id.

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, _, _ := 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")
        ))
);
{
  "rule_id": "rule_3d5ae6d4-6722-40c5-ac4b-0bd66ab7d363",
  "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
    }
  ],
  "created_at": "2025-03-17T08:31:24.97610651Z",
  "updated_at": "2025-03-17T08:31:24.976106635Z"
}

Start the run

Start a batch reconciliation with one_to_one. Do not send grouping_criteria. Use the upload id and rule id from the steps above.

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, _, _ := 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"
        ))
);
{
  "reconciliation_id": "recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58"
}

Read the result

Call Get reconciliation with the id from the start response. This sample finished with 2 matches and 8 unmatched rows.

curl -X GET "http://localhost:5001/reconciliation/recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58" \
  -H "X-blnk-key: <api-key>"
const response = await blnk.Reconciliation.get(
  'recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58',
);
recon, _, _ := client.Reconciliation.Get(
    "recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58",
)
response = blnk.reconciliation.get(
  "recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58",
)
ApiResponse<JsonNode> response = blnk.reconciliation().get(
    "recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58"
);
{
  "reconciliation_id": "recon_f1df3e68-fd8d-4566-8670-891c3c1e7d58",
  "upload_id": "upload_0cd4a87d-544c-4b7e-9232-f36e79631c32",
  "status": "completed",
  "matched_transactions": 2,
  "unmatched_transactions": 8,
  "started_at": "2025-03-17T08:32:24.005476Z",
  "completed_at": "2025-03-17T08:32:24.062136Z"
}
FieldDescription
matched_transactionsHow many external records matched.
unmatched_transactionsHow many external records did not match.