Connect your sandbox to see this tutorial in action.

Tutorials

Lending

Learn how to implement lending with Blnk.

A loan balance tracks what a customer owes. Disbursement moves the principal onto their main balance, interest increases what they owe, and a repayment pays it down.

This tutorial shows how to implement that with Blnk.

Step

Design the money movement

Before we start building, we need to define our money movement. This helps us outline how a loan is disbursed, charged, and repaid.

Alice borrows $500.00. The source is her loan balance, and the destination is her main balance. Overdraft lets the loan balance go negative, and that negative amount is what she owes.

One day later we charge $5.00 of interest from the loan balance to @InterestRevenue. Alice then repays $200.00 from her main balance back to the loan balance.

Step

Create the customer ledger

Before we start recording transactions, we need a ledger for customer main balances, separate from the ledger that holds what they owe.

For our lending example, we only support one currency, USD. Therefore, we’ll create a ledger to manage all USD main balances belonging to our customers.

Create the customer ledger:

curl -X POST "CORE_URL/ledgers" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{{name}}",
    "meta_data": {
      "description": "{{description}}"
    }
  }'
const response = await blnk.Ledgers.create({
  name: '{{name}}',
  meta_data: {
    description: '{{description}}',
  },
});
ledger, _, _ := client.Ledger.Create(blnkgo.CreateLedgerRequest{
    Name: "{{name}}",
    MetaData: blnkgo.MetaData{
        "description": "{{description}}",
    },
})
response = blnk.ledgers.create({
  "name": "{{name}}",
  "meta_data": {
    "description": "{{description}}",
  },
})
ApiResponse<JsonNode> response = blnk.ledgers().create(
    CreateLedger.create()
        .name("{{name}}")
        .metaData(Map.of(
            "description", "{{description}}"
        )));

Step

Create the loan ledger

The loan balances live in their own ledger, so we can roll up what customers owe without mixing it into their main balances.

@InterestRevenue is created in the General Ledger when we charge interest, separate from both of these customer ledgers.

Create the loan ledger:

curl -X POST "CORE_URL/ledgers" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "{{name}}",
    "meta_data": {
      "description": "{{description}}"
    }
  }'
const response = await blnk.Ledgers.create({
  name: '{{name}}',
  meta_data: {
    description: '{{description}}',
  },
});
ledger, _, _ := client.Ledger.Create(blnkgo.CreateLedgerRequest{
    Name: "{{name}}",
    MetaData: blnkgo.MetaData{
        "description": "{{description}}",
    },
})
response = blnk.ledgers.create({
  "name": "{{name}}",
  "meta_data": {
    "description": "{{description}}",
  },
})
ApiResponse<JsonNode> response = blnk.ledgers().create(
    CreateLedger.create()
        .name("{{name}}")
        .metaData(Map.of(
            "description", "{{description}}"
        )));

Step

Create an identity

An identity is how we record the customer. We pass Alice’s type, name, email, and phone number.

Anything else we want to keep goes in metadata. We’ll store her customer id there as customer_id. Both balances use this same identity.

Create the identity:

curl -X POST "CORE_URL/identities" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "identity_type": "individual",
    "first_name": "{{first_name}}",
    "last_name": "{{last_name}}",
    "email_address": "{{email_address}}",
    "phone_number": "{{phone_number}}",
    "meta_data": {
      "customer_id": "{{customer_id}}"
    }
  }'
const response = await blnk.Identity.create({
  identity_type: 'individual',
  first_name: '{{first_name}}',
  last_name: '{{last_name}}',
  email_address: '{{email_address}}',
  phone_number: '{{phone_number}}',
  meta_data: {
    customer_id: '{{customer_id}}',
  },
});
identity, _, _ := client.Identity.Create(blnkgo.Identity{
    IdentityType: blnkgo.Individual,
    FirstName: "{{first_name}}",
    LastName: "{{last_name}}",
    EmailAddress: "{{email_address}}",
    PhoneNumber: "{{phone_number}}",
    MetaData: blnkgo.MetaData{
        "customer_id": "{{customer_id}}",
    },
})
response = blnk.identity.create({
  "identity_type": "individual",
  "first_name": "{{first_name}}",
  "last_name": "{{last_name}}",
  "email_address": "{{email_address}}",
  "phone_number": "{{phone_number}}",
  "meta_data": {
    "customer_id": "{{customer_id}}",
  },
})
ApiResponse<JsonNode> response = blnk.identity().create(
    IdentityData.create()
        .identityType("individual")
        .firstName("{{first_name}}")
        .lastName("{{last_name}}")
        .emailAddress("{{email_address}}")
        .phoneNumber("{{phone_number}}")
        .metaData(Map.of("customer_id", "{{customer_id}}")));

Step

Create the main balance

The main balance is where Alice receives the loan and from where she repays it. It sits on the customer ledger and uses her identity.

We’ll use the customer ledger_id and identity_id from the earlier steps, set the currency, and submit.

Create the main balance:

curl -X POST "CORE_URL/balances" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ledger_id": "{{ledger_id}}",
    "identity_id": "{{identity_id}}",
    "currency": "{{currency}}"
  }'
const response = await blnk.LedgerBalances.create({
  ledger_id: '{{ledger_id}}',
  identity_id: '{{identity_id}}',
  currency: '{{currency}}',
});
balance, _, _ := client.LedgerBalance.Create(blnkgo.CreateLedgerBalanceRequest{
    LedgerID: "{{ledger_id}}",
    IdentityID: "{{identity_id}}",
    Currency: "{{currency}}",
})
response = blnk.ledger_balances.create({
  "ledger_id": "{{ledger_id}}",
  "identity_id": "{{identity_id}}",
  "currency": "{{currency}}",
})
ApiResponse<JsonNode> response = blnk.ledgerBalances().create(
    CreateLedgerBalance.create()
        .ledgerId("{{ledger_id}}")
        .identityId("{{identity_id}}")
        .currency("{{currency}}"));

Step

Create the loan balance

The loan balance is what Alice owes. A negative balance is debt, and a balance of 0 means the loan is repaid.

It sits on the loan ledger and uses the same identity as her main balance.

Create the loan balance:

curl -X POST "CORE_URL/balances" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ledger_id": "{{ledger_id}}",
    "identity_id": "{{identity_id}}",
    "currency": "{{currency}}"
  }'
const response = await blnk.LedgerBalances.create({
  ledger_id: '{{ledger_id}}',
  identity_id: '{{identity_id}}',
  currency: '{{currency}}',
});
balance, _, _ := client.LedgerBalance.Create(blnkgo.CreateLedgerBalanceRequest{
    LedgerID: "{{ledger_id}}",
    IdentityID: "{{identity_id}}",
    Currency: "{{currency}}",
})
response = blnk.ledger_balances.create({
  "ledger_id": "{{ledger_id}}",
  "identity_id": "{{identity_id}}",
  "currency": "{{currency}}",
})
ApiResponse<JsonNode> response = blnk.ledgerBalances().create(
    CreateLedgerBalance.create()
        .ledgerId("{{ledger_id}}")
        .identityId("{{identity_id}}")
        .currency("{{currency}}"));

Step

Disburse the loan

Alice borrows $500.00. We debit her loan balance and credit her main balance, so she receives the funds and the loan balance shows what she owes.

The amount is a decimal, and we multiply it by precision 100 to get precise_amount. The reference and the description are already set.

Disburse the loan:

curl -X POST "CORE_URL/transactions" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "precise_amount": {{precise_amount}},
    "precision": 100,
    "reference": "LOAN-20261019-103000",
    "description": "Loan disbursement",
    "currency": "{{currency}}",
    "source": "{{source}}",
    "destination": "{{destination}}",
    "allow_overdraft": true
  }'
const amount = {{amount}};
const precision = 100;
const precise_amount = Math.round(amount * precision);

const response = await blnk.Transactions.create({
  precise_amount,
  precision,
  reference: 'LOAN-20261019-103000',
  description: 'Loan disbursement',
  currency: '{{currency}}',
  source: '{{source}}',
  destination: '{{destination}}',
  allow_overdraft: true,
});
amount := {{amount}}
precision := 100
preciseAmount := big.NewInt(int64(math.Round(amount * float64(precision))))

transaction, _, _ := client.Transaction.Create(blnkgo.CreateTransactionRequest{
    ParentTransaction: blnkgo.ParentTransaction{
        PreciseAmount: preciseAmount,
        Precision: precision,
        Reference: "LOAN-20261019-103000",
        Description: "Loan disbursement",
        Currency: "{{currency}}",
        Source: "{{source}}",
        Destination: "{{destination}}",
    },
    AllowOverdraft: true,
})
amount = {{amount}}
precision = 100
precise_amount = round(amount * precision)

response = blnk.transactions.create({
  "precise_amount": precise_amount,
  "precision": precision,
  "reference": "LOAN-20261019-103000",
  "description": "Loan disbursement",
  "currency": "{{currency}}",
  "source": "{{source}}",
  "destination": "{{destination}}",
  "allow_overdraft": True,
})
double amount = {{amount}};
int precision = 100;
int preciseAmount = (int) Math.round(amount * precision);

ApiResponse<JsonNode> response = blnk.transactions().create(
    CreateTransactions.create()
        .preciseAmount(preciseAmount)
        .precision(precision)
        .reference("LOAN-20261019-103000")
        .description("Loan disbursement")
        .currency("{{currency}}")
        .source("{{source}}")
        .destination("{{destination}}")
        .allowOverdraft(true));

Step

Charge interest

Interest is money Alice owes on top of the principal. We debit the loan balance again and credit @InterestRevenue, an internal balance for interest earned.

For this example the charge is $5.00, which is 1% of the $500.00 principal for the day. The loan balance moves from -$500.00 to -$505.00.

Charge the interest:

curl -X POST "CORE_URL/transactions" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "precise_amount": {{precise_amount}},
    "precision": 100,
    "reference": "INT-20261019-103000",
    "description": "Daily interest charge",
    "currency": "{{currency}}",
    "source": "{{source}}",
    "destination": "{{destination}}",
    "allow_overdraft": true
  }'
const amount = {{amount}};
const precision = 100;
const precise_amount = Math.round(amount * precision);

const response = await blnk.Transactions.create({
  precise_amount,
  precision,
  reference: 'INT-20261019-103000',
  description: 'Daily interest charge',
  currency: '{{currency}}',
  source: '{{source}}',
  destination: '{{destination}}',
  allow_overdraft: true,
});
amount := {{amount}}
precision := 100
preciseAmount := big.NewInt(int64(math.Round(amount * float64(precision))))

transaction, _, _ := client.Transaction.Create(blnkgo.CreateTransactionRequest{
    ParentTransaction: blnkgo.ParentTransaction{
        PreciseAmount: preciseAmount,
        Precision: precision,
        Reference: "INT-20261019-103000",
        Description: "Daily interest charge",
        Currency: "{{currency}}",
        Source: "{{source}}",
        Destination: "{{destination}}",
    },
    AllowOverdraft: true,
})
amount = {{amount}}
precision = 100
precise_amount = round(amount * precision)

response = blnk.transactions.create({
  "precise_amount": precise_amount,
  "precision": precision,
  "reference": "INT-20261019-103000",
  "description": "Daily interest charge",
  "currency": "{{currency}}",
  "source": "{{source}}",
  "destination": "{{destination}}",
  "allow_overdraft": True,
})
double amount = {{amount}};
int precision = 100;
int preciseAmount = (int) Math.round(amount * precision);

ApiResponse<JsonNode> response = blnk.transactions().create(
    CreateTransactions.create()
        .preciseAmount(preciseAmount)
        .precision(precision)
        .reference("INT-20261019-103000")
        .description("Daily interest charge")
        .currency("{{currency}}")
        .source("{{source}}")
        .destination("{{destination}}")
        .allowOverdraft(true));

Step

Record a repayment

Alice repays $200.00 from her main balance. We debit that balance and credit the loan balance, which moves the loan balance from -$505.00 to -$305.00.

Her main balance has the $500.00 disbursement, so this repayment does not need overdraft.

Record the repayment:

curl -X POST "CORE_URL/transactions" \
  -H "X-blnk-key: CORE_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "precise_amount": {{precise_amount}},
    "precision": 100,
    "reference": "REPAY-20261019-103000",
    "description": "Loan repayment",
    "currency": "{{currency}}",
    "source": "{{source}}",
    "destination": "{{destination}}"
  }'
const amount = {{amount}};
const precision = 100;
const precise_amount = Math.round(amount * precision);

const response = await blnk.Transactions.create({
  precise_amount,
  precision,
  reference: 'REPAY-20261019-103000',
  description: 'Loan repayment',
  currency: '{{currency}}',
  source: '{{source}}',
  destination: '{{destination}}',
});
amount := {{amount}}
precision := 100
preciseAmount := big.NewInt(int64(math.Round(amount * float64(precision))))

transaction, _, _ := client.Transaction.Create(blnkgo.CreateTransactionRequest{
    ParentTransaction: blnkgo.ParentTransaction{
        PreciseAmount: preciseAmount,
        Precision: precision,
        Reference: "REPAY-20261019-103000",
        Description: "Loan repayment",
        Currency: "{{currency}}",
        Source: "{{source}}",
        Destination: "{{destination}}",
    },
})
amount = {{amount}}
precision = 100
precise_amount = round(amount * precision)

response = blnk.transactions.create({
  "precise_amount": precise_amount,
  "precision": precision,
  "reference": "REPAY-20261019-103000",
  "description": "Loan repayment",
  "currency": "{{currency}}",
  "source": "{{source}}",
  "destination": "{{destination}}",
})
double amount = {{amount}};
int precision = 100;
int preciseAmount = (int) Math.round(amount * precision);

ApiResponse<JsonNode> response = blnk.transactions().create(
    CreateTransactions.create()
        .preciseAmount(preciseAmount)
        .precision(precision)
        .reference("REPAY-20261019-103000")
        .description("Loan repayment")
        .currency("{{currency}}")
        .source("{{source}}")
        .destination("{{destination}}"));

Step

Confirmation

Alice’s main balance is $300.00. Her loan balance is -$305.00, which is the $500.00 she borrowed, plus $5.00 of interest, minus the $200.00 repayment.

If you have a Blnk Cloud account or an Enterprise license, you can confirm this by checking the dashboard or using the CLI.

Print every balance:

blnk balances list