Contents

Mia opens Kite, a personal finance app, sets a savings goal, and picks her terms: $200 a month for 3 months. She confirms it and moves on. From that point, she expects the deposits to happen on their own.

For you as the developer, a fixed savings goal gives you everything upfront: the amount, the deposit count, and the exact dates. In this guide, you'll set up Mia's savings balance, schedule all three deposits in a single bulk request, and query her progress, all with Blnk.

Mental model

Before you touch any code, define how money should move when Mia's goal is active. For Kite, there's one movement:

  • Deposit: @BankDepositsbln_mia_savings. Each scheduled deposit moves $200 from the bank deposit source into Mia's savings balance on its due date.

@BankDeposits is a system balance that represents external inflows. Blnk creates it automatically the first time you reference it. In production, you define this name to map to your actual funding source.

The money movement map shows $200 flowing from @BankDeposits into Mia's savings balance on the 1st of each month across three scheduled deposits. See it with our Money Movement Map tool.

Step 1: Setting up your ledger design

Make sure you have a deployed Blnk instance before you start.

For Mia's savings goal, you need three things in place before you can schedule any transactions.

To implement this with Blnk:

  1. Create a ledger called Savings Ledger to group all savings balances. Read docs.
  2. Create an identity for Mia so you can attach her savings balance to her profile. Read docs.
  3. Create a savings balance for Mia under the Savings Ledger, linked to her identity. Read docs.

Save the ledger_id, identity_id, and balance_id Blnk returns. You'll reference the balance_id as destination in every scheduled deposit, and the ledger and identity IDs when setting up savings for other customers.

Step 2: Activating a savings goal

When Mia activates her goal, your app has three inputs: the amount ($200 per deposit), the frequency (monthly), and the count (the number of deposits, 3 in this case). Use those to calculate the exact date for each deposit, then send all three transactions in one bulk request. Each transaction has a scheduled_for value set to the 1st of its respective month.

```

--CODE language-bash--

curl -X POST "http://YOUR_BLNK_URL/transactions/bulk" \
-H "Content-Type: application/json" \
 -H "X-blnk-key: YOUR_API_KEY" \
 -d '{
   "atomic": true,
   "inflight": false,
    "run_async": false,
      "transactions": [
     {
       "amount": 20000,
       "precision": 100,
       "currency": "USD",
       "reference": "save_mia_2026-03",
       "source": "@BankDeposits",
       "destination": "bln_mia_savings",
       "description": "Monthly savings deposit - March 2026",
       "allow_overdraft": true,
       "scheduled_for": "2026-03-01T08:00:00+00:00",
       "meta_data": {
         "goal_id": "goal_mia_001",
         "period": "2026-03"
       }
     },
     {
       "amount": 20000,
       "precision": 100,
       "currency": "USD",
       "reference": "save_mia_2026-04",
       "source": "@BankDeposits",
       "destination": "bln_mia_savings",
       "description": "Monthly savings deposit - April 2026",
       "allow_overdraft": true,
       "scheduled_for": "2026-04-01T08:00:00+00:00",
       "meta_data": {
         "goal_id": "goal_mia_001",
         "period": "2026-04"
       }
     },
     {
       "amount": 20000,
       "precision": 100,
       "currency": "USD",
       "reference": "save_mia_2026-05",
       "source": "@BankDeposits",
       "destination": "bln_mia_savings",
       "description": "Monthly savings deposit - May 2026",
       "allow_overdraft": true,
       "scheduled_for": "2026-05-01T08:00:00+00:00",
       "meta_data": {
         "goal_id": "goal_mia_001",
         "period": "2026-05"
       }
     }
   ]
 }'

```

Response:

```

--CODE language-json--

{
  "batch_id": "bulk_mia_goal_001",
   "status": "queued",
   "transaction_count": 3
}

```

Here's what happens:

  • All 3 transactions are accepted at once with status QUEUED. Blnk holds them and applies each one automatically when its scheduled_for time is reached.
  • Each reference is unique per period; it acts as an idempotency key. If your app sends the same request twice, Blnk will reject the duplicate.
  • run_async: false means Blnk processes the batch synchronously and returns only when all transactions are queued.
  • allow_overdraft: true on @BankDeposits lets the system balance go negative. This is the standard pattern for representing external inflows.
  • goal_id in meta_data tags every transaction in this goal to the same ID, so you can search all of them together.
Transactions table showing all three scheduled deposits for Mia's savings goal, each with status QUEUED and their respective scheduled dates.

Step 3: Tracking goal progress

To show Mia how much she's saved so far, read her savings balance:

```

--CODE language-json--
curl -X GET "http://YOUR_BLNK_URL/balances/bln_mia_savings" \
 -H "Content-Type: application/json" \
 -H "X-blnk-key: YOUR_API_KEY"

```

Response:

```

--CODE language-json--

{
   "balance_id": "bln_mia_savings",
   "balance": 40000,
   "credit_balance": 40000,
   "debit_balance": 0,
   "currency": "USD",
   "precision": 100,
   "ledger_id": "ldg_savings_001",
   "identity_id": "idt_mia_001",
   "created_at": "2026-03-01T08:00:00Z"
}

```

Here's what happens:

  • balance is returned in the smallest unit. Divide by precision (100) to get the display amount: 40000 / 100 = $400 saved so far (March and April deposits applied).
  • credit_balance reflects total money received into the balance; debit_balance reflects total money sent out. For a savings balance that only receives deposits, debit_balance stays at 0.

To see which deposits have run and which are still scheduled, query by goal_id:

```

--CODE language-json--

curl -X POST "http://YOUR_BLNK_URL/search/transactions" \\
 -H "Content-Type: application/json" \\
 -H "X-blnk-key: YOUR_API_KEY" \\
 -d '{
   "q": "goal_mia_001",
   "query_by": "meta_data.goal_id"
 }'

```

Response:

```

--CODE language-json--

{
   "found": 3,
   "hits": [
       {
           "document": {
               "transaction_id": "txn_save_mia_001",
               "reference": "save_mia_2026-03",
               "amount": 20000,
               "currency": "USD",
               "status": "APPLIED",
               "scheduled_for": "2026-03-01T08:00:00+00:00",
               "meta_data": { "goal_id": "goal_mia_001", "period": "2026-03" }
           }
       },
       {
           "document": {
               "transaction_id": "txn_save_mia_002",
               "reference": "save_mia_2026-04",
               "amount": 20000,
               "currency": "USD",
               "status": "APPLIED",
               "scheduled_for": "2026-04-01T08:00:00+00:00",
               "meta_data": { "goal_id": "goal_mia_001", "period": "2026-04" }
           }
      },
       {
           "document": {
               "transaction_id": "txn_save_mia_003",
               "reference": "save_mia_2026-05",
               "amount": 20000,
               "currency": "USD",
               "status": "QUEUED",
               "scheduled_for": "2026-05-01T08:00:00+00:00",
               "meta_data": { "goal_id": "goal_mia_001", "period": "2026-05" }
           }
       }
   ]
}

```

Here's what happens:

  • found is the total number of transactions in this goal. Each hit has a status: APPLIED for deposits that have run, QUEUED for ones still scheduled.

Wrapping up

You now have a savings goal that runs completely on schedule. Mia confirmed her goal once; Blnk handles the rest: applying each deposit on the right date, updating her balance, and keeping a full audit trail.

This same pattern works for any recurring payment with a known duration: installment plans, fixed-term subscriptions, scheduled loan repayments, or any flow where you know the amount and the count upfront.

To see this flow end-to-end, run the savings goal demo in the blnk-demo repo.

What else can you build?

With this foundation you can:

  • Pause a goal: identify which scheduled transactions haven't run yet and cancel them with reversals when the customer pauses.
  • Adjust the amount: cancel remaining scheduled transactions and recreate them with the updated amount.
  • Extend the goal: add more scheduled transactions to cover the new periods.
  • Notify on each deposit: listen for transaction.applied webhooks to send Mia a confirmation when each month's deposit lands.

For more on Blnk, check the Blnk docs or join the community on Discord to ask questions and see what others are building.