Tutorials
Escrow
Learn how to implement an escrow payment with Blnk.
An escrow payment is money a buyer sends to a seller that stays on hold until the sale is done. A commit releases it to the seller, and a void returns it to the buyer.
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 money moves from Alice to Bob.
Alice is buying from Bob, and the payment stays on hold until the sale is done. She sends $100.00 from her balance to his.
The transaction is inflight, so neither balance changes until we commit or void it. A commit applies the $100.00 to Bob, and a void drops the hold and leaves the money with Alice.
Step
Create a ledger
Before we start recording transactions, we need to set up our ledger to track the buyer, the seller, and their balances.
For our escrow example, we only support one currency, USD. Therefore, we’ll create a ledger to manage all USD balances belonging to our customers.
Create the 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 buyer
An identity is how we record Alice, the buyer. We pass her 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.
Create the buyer:
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 seller
Bob is the seller, so he gets his own identity. We pass his type, name, email, and phone number, and store his customer id as customer_id.
Create the seller:
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 buyer's wallet
Now that we have Alice in our ledger, we can create a balance for her, stored in the ledger from step 2.
We’ll use the ledger_id from that step and Alice’s identity_id, set the currency, and submit. Blnk creates the balance and attaches it to Alice.
Create the buyer’s wallet:
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 seller's wallet
Bob needs a balance in the same ledger to receive the payment. We’ll use his identity_id, set the currency, and submit.
Create the seller’s wallet:
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
Record a deposit
Alice deposits $100.00 via an ACH transfer so she can pay Bob. The destination is her balance, and the source is @WorldUSD_ACH.
The amount is a decimal, and we multiply it by precision 100 to get precise_amount. The reference and the description are already set.
Record the deposit:
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": "DEP-20261019-103000",
"description": "ACH deposit",
"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: 'DEP-20261019-103000',
description: 'ACH deposit',
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: "DEP-20261019-103000",
Description: "ACH deposit",
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": "DEP-20261019-103000",
"description": "ACH deposit",
"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("DEP-20261019-103000")
.description("ACH deposit")
.currency("{{currency}}")
.source("{{source}}")
.destination("{{destination}}")
.allowOverdraft(true));Step
Hold the payment
Alice pays Bob $100.00, and we hold it until the sale is done. The source is her balance, the destination is his, and inflight is on so neither balance changes yet.
The reference and the description are already set. Blnk records the pending amount on the inflight fields and leaves the main balances where they are.
Hold the payment:
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": "ESC-20261019-103000",
"description": "Escrow payment",
"currency": "{{currency}}",
"source": "{{source}}",
"destination": "{{destination}}",
"inflight": true
}'const amount = {{amount}};
const precision = 100;
const precise_amount = Math.round(amount * precision);
const response = await blnk.Transactions.create({
precise_amount,
precision,
reference: 'ESC-20261019-103000',
description: 'Escrow payment',
currency: '{{currency}}',
source: '{{source}}',
destination: '{{destination}}',
inflight: 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: "ESC-20261019-103000",
Description: "Escrow payment",
Currency: "{{currency}}",
Source: "{{source}}",
Destination: "{{destination}}",
},
Inflight: true,
})amount = {{amount}}
precision = 100
precise_amount = round(amount * precision)
response = blnk.transactions.create({
"precise_amount": precise_amount,
"precision": precision,
"reference": "ESC-20261019-103000",
"description": "Escrow payment",
"currency": "{{currency}}",
"source": "{{source}}",
"destination": "{{destination}}",
"inflight": 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("ESC-20261019-103000")
.description("Escrow payment")
.currency("{{currency}}")
.source("{{source}}")
.destination("{{destination}}")
.inflight(true));Step
Commit the hold
The sale is done, so we commit the hold. We send commit for the transaction from the previous step, and the $100.00 moves from Alice to Bob.
Commit the hold:
curl -X PUT "CORE_URL/transactions/inflight/{{transaction_id}}" \
-H "X-blnk-key: CORE_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "commit"
}'const response = await blnk.Transactions.updateStatus(
'{{transaction_id}}',
{
status: 'commit',
},
);transaction, _, _ := client.Transaction.Update(
"{{transaction_id}}",
blnkgo.UpdateStatus{
Status: blnkgo.InflightStatusCommit,
},
)response = blnk.transactions.update_status(
"{{transaction_id}}",
{
"status": "commit",
},
)ApiResponse<JsonNode> response = blnk.transactions().updateStatus(
"{{transaction_id}}",
UpdateTransactionStatus.create()
.status("commit"));Step
Confirmation
The commit is queued. When it is applied, Bob’s balance is $100.00 and Alice’s is 0.
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