Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.blnkfinance.com/llms.txt

Use this file to discover all available pages before exploring further.

This feature is in private beta. If you want access, please contact Support.
After installation, your app now has what it needs to work with the selected Cloud instance. The important values come from the install record you saved earlier:
  • api_key
  • instance_id
  • granted_permissions
Your backend should use those values to decide which instance to call, what key to use, and what actions the app is allowed to perform. For our Stripe Sync app, this is where the backend starts doing the work: importing pay-ins from Stripe, sending them to Blnk as transactions, and saving the sync details locally.

Choose the right Cloud API

Custom Apps usually use three classes of Cloud APIs.
APIWhen to use it
Data APIRead and filter ledger data through Cloud.
Proxy APIPerform Core actions through Cloud.
Other Cloud featuresUse Cloud features that are not Core endpoints, such as alerts.
All requests use the Cloud base URL:
https://api.cloud.blnkfinance.com
The installed app API key goes in the Authorization header:
Authorization: Bearer <api_key>
For Proxy and Data API requests, include the selected instance_id in the URL:
?instance_id=<instance_id>

Quick reference

Use the proxy when your app wants to call Blnk Core through Cloud. The format is:
https://api.cloud.blnkfinance.com/proxy/<core-endpoint>?instance_id=<instance_id>
The <core-endpoint> is the same endpoint you would normally call on Core, but with /proxy in front of it.For example, on Core, the request would look like this:
GET http://localhost:5001/ledgers
With the Cloud Proxy, it becomes:
GET https://api.cloud.blnkfinance.com/proxy/ledgers?instance_id=inst_...
A complete request via the Cloud Proxy would look like:
bash
curl -X GET "https://api.cloud.blnkfinance.com/proxy/ledgers?instance_id=inst_..." \
  -H "Authorization: Bearer blnk_..." \
  -H "Content-Type: application/json"

Using the proxy

Learn more about how the proxy works and see all the available endpoints.

Example application

Let’s apply this to build the Stripe Sync workflow we mapped earlier.
1

Fetch pay-ins from Stripe

First, we’ll list pay-ins from Stripe and keep only the ones that have actually settled (status === "succeeded"):
fetchStripePayIns.ts
import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

async function fetchStripePayIns() {
  const paymentIntents = await stripe.paymentIntents.list({ limit: 100 });

  return paymentIntents.data
    .filter((pi) => pi.status === "succeeded")
    .map((pi) => ({
      id: pi.id,
      amount: pi.amount_received,
      currency: pi.currency.toUpperCase(),
      customer: pi.customer as string,
      created_at: new Date(pi.created * 1000).toISOString(),
    }));
}
Filtering on status === "succeeded" ensures we only import pay-ins that have actually settled into your Stripe balance — anything still pending, requiring action, or canceled is skipped.
2

Send the pay-in to Blnk

Next, we’ll mirror the Stripe pay-in as a transaction in the selected Blnk Cloud instance through the Proxy API:
sendPayInToBlnk.ts
async function sendPayInToBlnk(instance_id: string, payIn) {
  const headers = {
    Authorization: `Bearer ${api_key}`,
    "Content-Type": "application/json",
  };

  const response = await axios.post(
    `https://api.cloud.blnkfinance.com/proxy/transactions?instance_id=${instance_id}`,
    {
      precise_amount: payIn.amount,
      precision: 100,
      reference: payIn.id,
      currency: payIn.currency,
      source: "@stripe",
      destination: `@${payIn.customer}`,
      description: "Imported pay-in from Stripe",
      effective_date: payIn.created_at,
      meta_data: {
        stripe_payment_intent_id: payIn.id,
        stripe_customer_id: payIn.customer,
      },
    },
    { headers }
  );

  return response.data;
}
We’ll use the Stripe payment intent ID directly as the reference in Blnk. Re-syncing the same payment intent will not create a duplicate transaction in Blnk because Blnk handles idempotency internally.
3

Save the sync details in your app

Finally, we’ll record the sync run in the app’s local database so we know when the sync ran, how many pay-ins it processed, and whether it succeeded:
saveSyncRecord.ts
async function saveSyncRecord(payIns, started_at) {
  await db.run(
    `INSERT INTO stripe_syncs (
      sync_id,
      records_found,
      started_at,
      completed_at
    ) VALUES (?, ?, ?, ?, ?)`,
    [
      crypto.randomUUID(),
      payIns.length,
      started_at,
      new Date().toISOString(),
    ]
  );
}
Call this once per sync run (after Step 2 completes for the whole batch) so each row represents one end-to-end sync, not each individual pay-in. The link back to specific Blnk transactions is already preserved by the meta_data.stripe_payment_intent_id written in Step 2.

Run the example Stripe Sync app

Open the demo repository and follow its README to run the Stripe Sync example app this documentation is built around.