# Backup to Disk Source: https://docs.blnkfinance.com/advanced/backup-disk Learn how to backup your Blnk data to a local disk. Ensuring the safety and integrity of your financial data on a Blnk server is crucial. This enhanced guide covers the process of backing up your PostgreSQL database, including setup instructions, verification, storage best practices, and restoration steps. *** ## Before you start Before initiating your first backup, ensure your Blnk server environment is correctly configured. This includes setting up the backup directory with appropriate permissions, ensuring your Blnk server has access to this directory, and updating the Blnk configuration file to recognize the backup directory. Here are the prerequisites: * Docker 20.10.11 or later * Compose 1.29.2 or later * Basic understanding of cron jobs (for Linux/macOS) or Task Scheduler (for Windows) * Familiarity with command-line interfaces and shell scripting *** ## Configuring the backup directory Choose a secure location on your server to store the backup files. For example, `/var/backups/blnk`. ```bash bash theme={"system"} mkdir -p /var/backups/blnk ``` Adjust the directory permissions to ensure that the Blnk server can write to this directory. ```bash bash theme={"system"} chown youruser:yourgroup /var/backups/blnk chmod 700 /var/backups/blnk ``` > Replace `youruser:yourgroup` with the user and group that your Blnk server runs as. This step is crucial for preventing unauthorized access to your backup files. Modify your `blnk.json` configuration file to include the path to the backup directory. This tells the Blnk server where to save the backup files. Open your `blnk.json` file in a text editor and add the `backup_dir` key with the path to your backup directory: ```json blnk.json theme={"system"} { ... "backup_dir": "/var/backups/blnk", ... } ``` Save the changes to your configuration file. This step ensures that when the Blnk server initiates a backup, it correctly locates the directory to store the backup files. *** ## Automating backups Automating the backup process ensures your data is regularly backed up without manual intervention. Create a script named `backup.sh` that triggers the Blnk server to back up to disk: ```bash Create backup script theme={"system"} curl -X GET http://localhost:5001/backup \ -H 'X-blnk-key: ' ``` Use cron (Linux/macOS) or Task Scheduler (Windows) to schedule the `backup.sh` script. For example, to run the backup daily at 2 AM: ```bash Schedule backup with Cron theme={"system"} chmod +x /path/to/backup.sh 0 2 * * * /path/to/backup.sh ``` *** ## Best practices for backup storage to disk * **Off-site Storage**: Regularly copy your backups to an off-site location or cloud storage to protect against local disasters. * **Encryption**: Encrypt your backup files to protect sensitive data from unauthorized access. * **Retention Policy**: Implement a retention policy to manage the lifecycle of your backups, ensuring that you keep only what's necessary and avoid unnecessary storage costs. *** ## Restoring from a Backup In the event of data loss or corruption, having a reliable backup is crucial. Here's how to restore your Blnk database from a backup file: Identify the most recent or appropriate backup file for your restoration needs. Use the PostgreSQL `pg_restore` command or a similar tool provided by Blnk to restore your database from the backup file. ```bash bash theme={"system"} pg_restore -d yourdatabase /path/to/yourbackupfile ``` Ensure that the restoration process was successful and that your Blnk application is functioning correctly. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Backup to Amazon S3 Source: https://docs.blnkfinance.com/advanced/backup-s3 Learn how to backup your Blnk data to the cloud. Safeguarding your financial data on a Blnk server involves not only local backups but also utilizing cloud storage solutions like Amazon S3 for offsite storage. This guide provides detailed instructions on setting up backups to Amazon S3, including configuration steps, scheduling, and verification processes. *** ## Prerequisites * Docker 20.10.11 or later; * Compose 1.29.2 or later; * AWS account with S3 access; * Basic understanding of cron jobs (for Linux/macOS) or Task Scheduler (for Windows); * Familiarity with command-line interfaces and shell scripting. *** ## Configuring Amazon S3 Access Before setting up the backup process, you need to configure your Blnk server to access your Amazon S3 bucket. 1. **Log in** to your AWS Management Console and navigate to the IAM (Identity and Access Management) service. 2. **Create a new IAM user** with programmatic access. This will generate an access key ID and secret access key, which you'll use to configure the Blnk server. 3. **Attach a policy** to the IAM user that grants necessary permissions to write to the S3 bucket. You can use the AmazonS3FullAccess policy or create a custom policy with more restricted access. 1. **Create an S3 bucket** in your AWS account if you have not already. Note the bucket name and region. 2. **Set up bucket policies** or access control lists (ACLs) as necessary to allow the IAM user to write backups to the bucket. Modify your `blnk.json` configuration file to include the AWS access key ID, secret access key, bucket name, and region. This enables the Blnk server to authenticate with AWS S3 and store backups in your bucket. ```json blnk.json theme={"system"} { ... "aws_access_key_id": "YOUR_ACCESS_KEY_ID", "aws_secret_access_key": "YOUR_SECRET_ACCESS_KEY", "s3_bucket_name": "YOUR_BUCKET_NAME", "s3_region": "YOUR_BUCKET_REGION", ... } ``` *** ## Automating S3 backups After configuring access to Amazon S3, the next step is to automate the backup process. Create a script named `backup_to_s3.sh` that triggers the Blnk server to back up to S3: ```bash Create backup to S3 script theme={"system"} curl -X GET http://localhost:5001/backup-s3 \ -H 'X-blnk-key: ' ``` Use cron (Linux/macOS) or Task Scheduler (Windows) to schedule the `backup_to_s3.sh` script. For example, to run the backup daily at 2 AM: ```bash Schedule S3 backup with Cron theme={"system"} chmod +x /path/to/backup_to_s3.sh 0 2 * * * /path/to/backup_to_s3.sh ``` *** ## Best practices for backup storage to Amazon S3 * **Encryption**: Enable server-side encryption (SSE) for your S3 bucket to protect your backups at rest. * **Versioning**: Enable versioning for your S3 bucket to keep multiple versions of your backups, providing additional protection against accidental deletion or overwriting. * **Lifecycle policies**: Implement lifecycle policies to automatically transition older backups to more cost-effective storage classes and purge outdated backups. *** ## Restoring from an S3 Backup To restore your Blnk database from a backup stored in S3: Use the AWS CLI, S3 console, or an S3 client to download the desired backup file from your S3 bucket. Follow the same steps as local restoration, using the `pg_restore` command with the path to the downloaded backup file. ```bash bash theme={"system"} pg_restore -d yourdatabase /path/to/downloaded_backup_file ``` Ensure the Blnk application functions correctly and all data is intact post-restoration. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Balance Reconstruction Source: https://docs.blnkfinance.com/advanced/balance-reconstruction Reconstruct and correct discrepancies in your ledger balances. Available on version 0.10.1 or later. Balance Reconstruction helps you solve situations where your balances go out of sync and you have to rebuild the balance from the ground up. It does this by recalculating a balance from its transactions to ensure it correctly reflects all recorded activity. *** ## Reconstructing a balance To reconstruct a balance, call the [Update Metadata](/reference/update-metadata) endpoint with the balance ID and set `BLNK_RUN_RECONCILIATION` to `SOURCE`: ```bash cURL wrap theme={"system"} curl -X POST "http://YOUR_BLNK_INSTANCE_URL/{balance_id}/metadata" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "meta_data": { "BLNK_RUN_RECONCILIATION": "SOURCE" } }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.Metadata.update( 'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f', { meta_data: { BLNK_RUN_RECONCILIATION: 'SOURCE', }, }, ); ``` ```go Go wrap theme={"system"} meta, resp, err := client.Metadata.UpdateMetadata( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", blnkgo.UpdateMetaDataRequest{ MetaData: blnkgo.MetaData{ "BLNK_RUN_RECONCILIATION": "SOURCE", }, }, ) ``` ```python Python wrap theme={"system"} response = blnk.metadata.update( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", { "meta_data": { "BLNK_RUN_RECONCILIATION": "SOURCE", }, }, ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.metadata().update( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", UpdateMetadataData.create() .metaData(Map.of("BLNK_RUN_RECONCILIATION", "SOURCE"))); ``` ```json Response theme={"system"} { "meta_data": { "BLNK_RUN_RECONCILIATION": "SOURCE" } } ``` *** ## Verifying the results To check the results, retrieve the balance details with the [Get Balance](/reference/get-balance) endpoint: ```bash cURL wrap theme={"system"} curl -X GET "http://YOUR_BLNK_INSTANCE_URL/balances/{balance_id}" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.LedgerBalances.get( 'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f', ); ``` ```go Go wrap theme={"system"} balance, resp, err := client.LedgerBalance.Get("bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f") ``` ```python Python wrap theme={"system"} response = blnk.ledger_balances.get( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.ledgerBalances().get("bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f"); ``` The results will be available in the `meta_data` object: ```json Response theme={"system"} { "meta_data": { "BLNK_RECONCILIATION_RESULT": { "difference": "103842", "executed_at": "2025-03-16 22:55:34.281624+00", "previous_balance": "600", "previous_credit": "1200", "previous_debit": "600", "recalculated_balance": "104442", "recalculated_credit": "119600", "recalculated_debit": "15158" } } } ``` | Field | Description | | :--------------------- | :------------------------------------------------------------------------- | | `difference` | The discrepancy between the previous balance and the recalculated balance. | | `executed_at` | The timestamp when the reconstruction was performed. | | `previous_balance` | The balance recorded before reconstruction. | | `previous_credit` | The total credit amount recorded before reconstruction. | | `previous_debit` | The total debit amount recorded before reconstruction. | | `recalculated_balance` | The corrected balance after reconstruction. | | `recalculated_credit` | The total credit amount after recalculating from transactions. | | `recalculated_debit` | The total debit amount after recalculating from transactions. | *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Backup Configuration Source: https://docs.blnkfinance.com/advanced/configuration/backup Configure backup paths and S3 settings for Blnk backups. This page covers the settings Blnk uses for backup storage, including the local backup path and the credentials needed for S3-compatible object storage. *** ## Backup settings Use these settings to choose where backup files are written and how Blnk connects to S3 or another S3-compatible service. ```bash blnk.env theme={"system"} BLNK_BACKUP_DIR= BLNK_AWS_ACCESS_KEY_ID= BLNK_AWS_SECRET_ACCESS_KEY= BLNK_S3_ENDPOINT= BLNK_S3_BUCKET_NAME= BLNK_S3_REGION= ``` ```json blnk.json theme={"system"} { "backup_dir": "", "aws_access_key_id": "", "aws_secret_access_key": "", "s3_endpoint": "", "s3_bucket_name": "", "s3_region": "" } ``` | | Description | Default | | :--------------------------- | :-------------------------------------------------------------------- | :------ | | `BLNK_BACKUP_DIR` | Local directory path Blnk uses when writing backup files. | None | | `BLNK_AWS_ACCESS_KEY_ID` | Access key ID used to authenticate with S3. | None | | `BLNK_AWS_SECRET_ACCESS_KEY` | Secret access key used to authenticate with S3. | None | | `BLNK_S3_ENDPOINT` | Custom S3 endpoint, usually used for S3-compatible storage providers. | None | | `BLNK_S3_BUCKET_NAME` | Bucket name where backup files are stored. | None | | `BLNK_S3_REGION` | Region for the S3 bucket. | None | **Important:** Store `BLNK_AWS_ACCESS_KEY_ID` and `BLNK_AWS_SECRET_ACCESS_KEY` as secrets. Do not commit them to version control. ### `BLNK_BACKUP_DIR` Use this to tell Blnk where backup files should be written before they are stored or uploaded. Set a path that your Blnk process can read from and write to. If you are running Blnk in containers, make sure the directory is backed by a mounted volume instead of short-lived container storage. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Data Store Configuration Source: https://docs.blnkfinance.com/advanced/configuration/data-stores Set up PostgreSQL, Redis, and Typesense for your Blnk deployment. Blnk relies on PostgreSQL and Redis to run core ledger operations, and can optionally use TypeSense for search and indexing. This page explains the settings used to connect and tune each service. *** ## Database settings Use these settings to configure Blnk’s PostgreSQL connection and connection pool. **Note:** Blnk only supports PostgreSQL as its database engine. You cannot use other database engines like MySQL or SQLite with Blnk. In `blnk.json`, `conn_max_lifetime` and `conn_max_idle_time` are integer seconds. Environment variables accept Go duration format (e.g. `30m`, `5m`). ```bash blnk.env theme={"system"} BLNK_DATA_SOURCE_DNS="postgres://user:pass@postgres:5432/blnk?sslmode=disable" BLNK_DATABASE_MAX_OPEN_CONNS=50 BLNK_DATABASE_MAX_IDLE_CONNS=25 BLNK_DATABASE_CONN_MAX_LIFETIME=30m BLNK_DATABASE_CONN_MAX_IDLE_TIME=5m ``` ```json blnk.json theme={"system"} { "data_source": { "dns": "postgres://user:pass@postgres:5432/blnk?sslmode=disable", "max_open_conns": 50, "max_idle_conns": 25, "conn_max_lifetime": 1800, "conn_max_idle_time": 300 } } ``` | | Description | Default | | ---------------------------------- | --------------------------------------------------------------------------------- | ------- | | `BLNK_DATA_SOURCE_DNS` | PostgreSQL connection string used by Blnk. This is required. | None | | `BLNK_DATABASE_MAX_OPEN_CONNS` | Maximum number of open database connections in the pool. | `50` | | `BLNK_DATABASE_MAX_IDLE_CONNS` | Maximum number of idle database connections kept ready. | `25` | | `BLNK_DATABASE_CONN_MAX_LIFETIME` | Maximum amount of time a database connection can be reused before it is recycled. | `30m` | | `BLNK_DATABASE_CONN_MAX_IDLE_TIME` | Maximum amount of time an idle database connection is kept before it is recycled. | `5m` | ### `BLNK_DATA_SOURCE_DNS` This is the PostgreSQL connection string Blnk uses to connect to its database. **This setting is required.** Blnk will not start without it. 1. Make sure the database is reachable from the environment where Blnk is running. This includes verifying the hostname, port, credentials, and network access rules before starting the server. 2. You should also make sure the `sslmode` in the connection string matches what your database provider expects. If this is set incorrectly, Blnk may fail to connect even when the rest of the connection string is valid. Some hosted PostgreSQL providers expose more than one connection URL. For application workloads, use the direct PostgreSQL connection string that is intended for normal client connections. ### Best practices 1. Always set `BLNK_DATA_SOURCE_DNS` explicitly. 2. Start with the default pool settings unless you already know your database limits. 3. Increase `BLNK_DATABASE_MAX_OPEN_CONNS` only if your PostgreSQL instance can handle more concurrent connections. 4. Use the connection lifetime and idle time settings to recycle stale connections in long-running deployments. *** ## Redis configuration ```bash blnk.env theme={"system"} BLNK_REDIS_DNS=redis://:password@redis:6379/0 BLNK_REDIS_SKIP_TLS_VERIFY=false ``` ```json blnk.json theme={"system"} { "redis": { "dns": "redis://:password@redis:6379/0", "skip_tls_verify": false } } ``` | | Description | Default | | ---------------------------- | --------------------------------------------------------------- | ------- | | `BLNK_REDIS_DNS` | Redis address or connection URL used by Blnk. This is required. | None | | `BLNK_REDIS_SKIP_TLS_VERIFY` | Disables TLS certificate verification for Redis connections. | `false` | ### `BLNK_REDIS_DNS` `BLNK_REDIS_DNS` is the Redis address or connection URL Blnk uses for queueing and coordination. **This setting is required.** Why? Because Blnk uses Redis for distributed balance locks, transaction and webhook queues, hot-pair state, and other worker coordination. If Redis is unavailable, queue processing and lock coordination are affected even if PostgreSQL is healthy. Use a full Redis URL when authentication or TLS is involved. For TLS-enabled Redis connections, make sure the URL starts with `rediss://`. ### `BLNK_REDIS_SKIP_TLS_VERIFY` This disables certificate verification for TLS Redis connections. Only set to `true` when you are working in a controlled environment and understand the risk. Disabling certificate verification weakens transport security and should not be the default for production deployments. *** ## Typesense configuration Use these settings to enable Blnk search and indexing via Typesense. In `blnk.json`, `backpressure_retry_interval` is a duration in **nanoseconds** (for example `5000000000` for 5s). The environment variable accepts Go duration format (for example `5s`). Omit the field to use the default. ```bash blnk.env theme={"system"} BLNK_TYPESENSE_DNS=http://typesense:8108 BLNK_TYPESENSE_KEY=blnk-api-key BLNK_TYPESENSE_BACKPRESSURE_RETRY_INTERVAL=5s ``` ```json blnk.json theme={"system"} { "typesense": { "dns": "http://typesense:8108", "backpressure_retry_interval": 5000000000 }, "type_sense_key": "blnk-api-key" } ``` | | Description | Default | | :------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- | :------------------ | | `BLNK_TYPESENSE_DNS` | TypeSense server address. If unset, Blnk disables search and indexing features via Typesense. | Disabled when unset | | `BLNK_TYPESENSE_KEY` | API key used to connect to TypeSense. | `blnk-api-key` | | `BLNK_TYPESENSE_BACKPRESSURE_RETRY_INTERVAL` | How long Blnk waits before retrying indexing or reindex work after Typesense reports a memory limit error. | `5s` | | `TYPESENSE_MEMORY_USED_MAX_PERCENTAGE` | Typesense container setting. Caps Typesense memory usage as a percentage of available memory before it rejects writes. | `85` | Set Typesense memory limits on the Typesense service itself, not in Blnk: ```bash typesense.env theme={"system"} TYPESENSE_MEMORY_USED_MAX_PERCENTAGE=85 ``` ### When to use Typesense TypeSense is optional. If it is not configured, Blnk starts just fine, but search and indexing features via Typesense are disabled. For most search use cases, start with the [Direct DB filter API](/search/db/filtering). It is a better fit when you need precise database-backed filtering, sorting, and pagination without running a separate search service. Use Typesense when you need full-text search, faceted filtering, faster queries across very large datasets, or search-oriented workflows that benefit from a dedicated index. **Tip:** If you are using TypeSense, we recommend `BLNK_TYPESENSE_KEY` explicitly instead of relying on the default key. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Observability Configuration Source: https://docs.blnkfinance.com/advanced/configuration/observability Enable observability and configure Cloud DSN and OTLP endpoints in Blnk. This page lists the flags that control **operational observability** in Blnk Core. For how to think about logs, traces, and metrics, and for setup walkthroughs for Blnk Cloud and self-hosted export, see [Monitoring in Blnk](/advanced/monitoring). These settings are independent from [queue monitoring](/advanced/monitoring-port), which exposes a separate HTTP port for queue inspection. *** ## Observability flags ```bash blnk.env theme={"system"} BLNK_ENABLE_OBSERVABILITY=false BLNK_CLOUD_DSN= ``` ```json blnk.json theme={"system"} { "enable_observability": false, "cloud_dsn": "" } ``` | | Outcome when set | Default | | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------ | | `BLNK_ENABLE_OBSERVABILITY` | Core emits logs, traces, and metrics; `/metrics` is registered on the API port and worker monitoring port. | `false` | | `BLNK_CLOUD_DSN` | Blnk Cloud DSN. Forwards logs, traces, and metrics to your Blnk Cloud monitoring project. Requires observability to be on. Format: `https://@/` | none | For `/metrics` bearer authentication, see [Server & security](/advanced/configuration/server-security#metrics-endpoint). `BLNK_CLOUD_DSN` has no effect when `BLNK_ENABLE_OBSERVABILITY` is `false`. Exported logs have sensitive values redacted before they leave your instance. *** ## Blnk Cloud DSN Set the Blnk Cloud DSN when you export logs, traces, and metrics to Blnk Cloud monitoring: * Environment: `BLNK_CLOUD_DSN` * JSON: `cloud_dsn` Requires `enable_observability=true` on the server and workers. See [Quick setup: Blnk Cloud](/advanced/monitoring#quick-setup-blnk-cloud) in [Monitoring in Blnk](/advanced/monitoring). *** ## OpenTelemetry endpoints For self-hosted export, Blnk reads standard OpenTelemetry environment variables. Set them on **both** the server and worker. Observability must be enabled. Blnk exports traces and metrics over **OTLP HTTP** (port **4318** in typical Jaeger or Collector setups, not gRPC port 4317). Set a separate endpoint per signal on **both** the server and worker. Observability must be enabled. ```bash blnk.env theme={"system"} OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://jaeger:4318 OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://otel-collector:4318 ``` These variables are environment-only. They are not set in `blnk.json`. | | Outcome when set | | :------------------------------------ | :----------------------------------------- | | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | OTLP HTTP endpoint for trace export. | | `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` | OTLP HTTP endpoint for metric push export. | Endpoint values accept common formats: `host:port`, `http://host:port`, or a full path such as `http://jaeger:4318/v1/traces`. Blnk normalizes the value before export. Metrics push runs on a 60-second interval. Prometheus-compatible metrics are also available at `/metrics` on the API port and worker monitoring port regardless of OTLP settings. See [Connect to other tools](/advanced/monitoring#connect-to-other-tools) in [Monitoring in Blnk](/advanced/monitoring). Logs are not exported through these endpoints. Collect process output with your log shipper, or set the Blnk Cloud DSN for managed log export. *** ## Product telemetry `enable_telemetry` is not operational observability. Turn on `enable_observability` for logs, traces, and metrics in Prometheus, Jaeger, or Blnk Cloud. `enable_telemetry` is optional product usage heartbeat telemetry for the Blnk team. ```bash blnk.env theme={"system"} BLNK_ENABLE_TELEMETRY=false ``` ```json blnk.json theme={"system"} { "enable_telemetry": false } ``` | | Description | Default | | :---------------------- | :--------------------------------------------------------------------------------------------------------------- | :------ | | `BLNK_ENABLE_TELEMETRY` | When `true`, Blnk sends optional product usage heartbeat telemetry so the Blnk team can understand usage trends. | `false` | *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Overview Source: https://docs.blnkfinance.com/advanced/configuration/overview Learn how Blnk configuration works with blnk.json and environment variables. This is the documentation for Blnk's configuration file. This file defines how your instance runs, including database and Redis connections, transaction processing, security, notifications, observability, and backups. You can set it up using a `blnk.json` file or environment variables that use the `BLNK_` prefix. *** ## Configuration structure Blnk's configuration is grouped into the following sections. Click through to learn more about each section and its configuration options. Runtime, secure mode, rate limits, and request size caps. PostgreSQL, Redis, and Typesense. Processing, batching, locking, coalescing. Names, sharding, retries, hot lanes. Strategy, progress updates, retries. Slack and outgoing HTTP webhooks. Logs, traces, metrics, and monitoring setup. Flags and environment variables. Local paths and S3 storage. Required fields, defaults, options. *** ## Minimum required settings To start Blnk, you only need to set your PostgreSQL and Redis connection URLs. Most of the other configuration settings have [sensible defaults](/advanced/configuration/reference) that you can leave as-is until you need to change them. ```bash blnk.env theme={"system"} # Database configuration BLNK_DATA_SOURCE_DNS="postgres://postgres:password@postgres:5432/blnk?sslmode=disable" # Redis configuration BLNK_REDIS_DNS="redis:6379" ``` ```json blnk.json theme={"system"} { "data_source": { "dns": "postgres://postgres:password@postgres:5432/blnk?sslmode=disable" }, "redis": { "dns": "redis:6379" } } ``` *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Queue Configuration Source: https://docs.blnkfinance.com/advanced/configuration/queue Configure queue names, sharding, concurrency, retries, hot-lane routing, and worker monitoring in Blnk. This page covers the settings that control how Blnk queues, routes, retries, and monitors background work for transactions, webhooks, indexing, inflight expiry, and scheduled inflight commits. For transaction batching, locking, and coalescing settings, see [Transaction configuration](/advanced/configuration/transactions). *** ## Queue settings Use these settings to control queue names, sharding, concurrency, retries, and worker monitoring for normal queued processing. ```bash blnk.env theme={"system"} BLNK_QUEUE_TRANSACTION=new:transaction BLNK_QUEUE_NUMBER_OF_QUEUES=20 BLNK_QUEUE_TRANSACTION_WORKER_CONCURRENCY=4 BLNK_QUEUE_MAX_RETRY_ATTEMPTS=5 BLNK_QUEUE_INSUFFICIENT_FUND_RETRIES=false BLNK_QUEUE_WEBHOOK=new:webhook BLNK_QUEUE_INDEX=new:index BLNK_QUEUE_INFLIGHT_EXPIRY=new:inflight-expiry BLNK_QUEUE_INFLIGHT_COMMIT=new:inflight-commit BLNK_QUEUE_WEBHOOK_CONCURRENCY=20 BLNK_QUEUE_MONITORING_PORT=5004 ``` ```json blnk.json theme={"system"} { "queue": { "transaction_queue": "new:transaction", "number_of_queues": 20, "transaction_worker_concurrency": 4, "max_retry_attempts": 5, "insufficient_fund_retries": false, "webhook_queue": "new:webhook", "index_queue": "new:index", "inflight_expiry_queue": "new:inflight-expiry", "inflight_commit_queue": "new:inflight-commit", "webhook_concurrency": 20, "monitoring_port": "5004" } } ``` | Variable | Default | Description | | ------------------------------------------- | --------------------- | ----------------------------------------------------------------------------------------- | | `BLNK_QUEUE_TRANSACTION` | `new:transaction` | Base name used for normal transaction queue shards. | | `BLNK_QUEUE_NUMBER_OF_QUEUES` | `20` | Number of normal transaction queue shards used for queued work. | | `BLNK_QUEUE_TRANSACTION_WORKER_CONCURRENCY` | `4` | Worker concurrency for normal transaction queues. | | `BLNK_QUEUE_MAX_RETRY_ATTEMPTS` | `5` | Maximum number of retries for queued transaction failures. | | `BLNK_QUEUE_INSUFFICIENT_FUND_RETRIES` | `false` | Controls whether insufficient-funds failures are retried instead of rejected immediately. | | `BLNK_QUEUE_WEBHOOK` | `new:webhook` | Queue name used for webhook tasks. | | `BLNK_QUEUE_INDEX` | `new:index` | Queue name used for indexing tasks. | | `BLNK_QUEUE_INFLIGHT_EXPIRY` | `new:inflight-expiry` | Queue name used for inflight-expiry tasks. | | `BLNK_QUEUE_INFLIGHT_COMMIT` | `new:inflight-commit` | Queue name used for scheduled inflight-commit tasks. | | `BLNK_QUEUE_WEBHOOK_CONCURRENCY` | `20` | Worker concurrency for webhook and index tasks. | | `BLNK_QUEUE_MONITORING_PORT` | `5004` | Port used for worker monitoring and metrics. | ### `BLNK_QUEUE_NUMBER_OF_QUEUES` Blnk hashes the source balance ID and assigns the transaction to one of the configured queue shards. More queues allow more unrelated balances to process in parallel. This helps reduce collisions before execution, but it does not replace locking. If many transactions still target the same balances, increasing the shard count alone will not solve the hotspot. See instead: [Sharding balances](/guides/hot-balances#sharding-balances). ### `BLNK_QUEUE_TRANSACTION_WORKER_CONCURRENCY` This controls how many normal queued transaction tasks can execute at the same time. Higher concurrency can improve throughput when work is spread across unrelated balances. It can also increase lock contention when many tasks overlap on the same balances. Increase it gradually and watch for contention before raising it further. See also: [Sharding balances](/guides/hot-balances#sharding-balances). ### `BLNK_QUEUE_MAX_RETRY_ATTEMPTS` This sets the maximum number of times Blnk will try a queued transaction again after a temporary failure such as lock contention, insufficient funds, etc. In simple terms: * If a queued transaction fails for a reason that may clear on its own, Blnk can try it again * If it keeps failing, Blnk stops after this limit instead of retrying forever. ### `BLNK_QUEUE_INSUFFICIENT_FUND_RETRIES` This controls whether Blnk should retry queued transactions that fail because the source balance does not have enough funds. Leave this disabled when an insufficient-funds result should be treated as final. Enable it only when the balance may change shortly after the first attempt, for example: * another queued credit is still being processed * funds are expected to arrive from another part of your workflow * transaction ordering means the balance may be sufficient on a later retry *** ## Backpressure settings When Redis memory or pending task count exceeds configured limits, Blnk rejects new queue enqueues and returns `503 Service Unavailable` with `QUEUE_BACKPRESSURE`. See [API error codes](/advanced/error-codes). Backpressure is enabled by default. In `blnk.json`, `backpressure_check_interval` is a duration in **nanoseconds** (for example `500000000` for 500ms). The environment variable accepts Go duration format (for example `500ms`). Omit the field to use the default. ```bash blnk.env theme={"system"} BLNK_QUEUE_ENABLE_BACKPRESSURE=true BLNK_QUEUE_BACKPRESSURE_MEMORY_PERCENT=85 BLNK_QUEUE_BACKPRESSURE_MAX_PENDING_TASKS=0 BLNK_QUEUE_BACKPRESSURE_CHECK_INTERVAL=500ms ``` ```json blnk.json theme={"system"} { "queue": { "enable_backpressure": true, "backpressure_memory_percent": 85, "backpressure_max_pending_tasks": 0, "backpressure_check_interval": 500000000 } } ``` | Variable | Default | Description | | ------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------- | | `BLNK_QUEUE_ENABLE_BACKPRESSURE` | `true` | Rejects new enqueues when backpressure limits are exceeded. | | `BLNK_QUEUE_BACKPRESSURE_MEMORY_PERCENT` | `85` | Rejects enqueues when Redis `used_memory` reaches this percentage of `maxmemory`. Applies only when Redis `maxmemory` is set. | | `BLNK_QUEUE_BACKPRESSURE_MAX_PENDING_TASKS` | `0` | Rejects enqueues when total pending transaction queue tasks reach this count. `0` disables the pending-task check. | | `BLNK_QUEUE_BACKPRESSURE_CHECK_INTERVAL` | `500ms` | Minimum time between backpressure checks during enqueue. | Redis `maxmemory` is a server setting, not a Blnk option. Set it in `redis.conf`, your container startup command (for example `redis-server --maxmemory 256mb`), or through your managed Redis provider. If `maxmemory` is unset, Blnk skips the memory check and only pending-task backpressure applies when configured. *** ## Hot-lane routing settings Use these settings to isolate repeatedly contended balance pairs into a dedicated queue. ```bash blnk.env theme={"system"} BLNK_QUEUE_ENABLE_HOT_LANE=false BLNK_QUEUE_HOT_QUEUE_NAME=hot_transactions BLNK_QUEUE_HOT_QUEUE_CONCURRENCY=1 BLNK_QUEUE_HOT_PAIR_TTL=300 BLNK_QUEUE_HOT_PAIR_LOCK_CONTENTION_THRESHOLD=3 BLNK_QUEUE_REJECT_LOCK_CONTENTION_IMMEDIATELY=false ``` ```json blnk.json theme={"system"} { "queue": { "enable_hot_lane": false, "hot_queue_name": "hot_transactions", "hot_queue_concurrency": 1, "hot_pair_ttl": 300, "hot_pair_lock_contention_threshold": 3, "reject_lock_contention_immediately": false } } ``` | Variable | Default | Description | | ----------------------------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------- | | `BLNK_QUEUE_ENABLE_HOT_LANE` | `false` | Enables hot-lane routing for repeatedly contended balance pairs. | | `BLNK_QUEUE_HOT_QUEUE_NAME` | `hot_transactions` | Queue name used for hot-lane traffic. | | `BLNK_QUEUE_HOT_QUEUE_CONCURRENCY` | `1` | Worker concurrency for the hot queue. | | `BLNK_QUEUE_HOT_PAIR_TTL` | `300` | How long hot-pair activity and contention state is remembered, in **seconds** (see note at top of this page). | | `BLNK_QUEUE_HOT_PAIR_LOCK_CONTENTION_THRESHOLD` | `3` | Number of contention events required before a pair is promoted into the hot lane. | | `BLNK_QUEUE_REJECT_LOCK_CONTENTION_IMMEDIATELY` | `false` | Rejects queued transactions immediately after lock-contention failure instead of retrying them. | ### `BLNK_QUEUE_ENABLE_HOT_LANE` Hot-lane routing is Blnk’s contention-aware queue routing strategy. Blnk tracks repeated lock-contention events for a specific `source|destination|currency` pair. When contention crosses the configured threshold, `BLNK_QUEUE_HOT_PAIR_LOCK_CONTENTION_THRESHOLD`, Blnk promotes that pair into a hot state. New queued transactions for that pair are then routed to a dedicated hot queue instead of the normal queue shards. This helps isolate the hottest balance pairs from the rest of your queued traffic, so they stop disturbing normal queue processing. ### `BLNK_QUEUE_REJECT_LOCK_CONTENTION_IMMEDIATELY` This setting changes what queued workers do after a lock-contention failure. When set to `true`, Blnk rejects the transaction immediately if the required lock is busy. When set to `false`, the worker treats the failure as retryable and retries it up to `BLNK_QUEUE_MAX_RETRY_ATTEMPTS`. **Please note:** This setting does not affect `skip_queue=true` transactions. ### Best practices * Enable hot-lane routing when only a few balance pairs keep colliding. * Use a shorter TTL for short bursts so pairs return to the normal queue sooner after traffic settles. Use a longer TTL when the same pairs stay hot for longer periods. * Lower the threshold if hot pairs are not moving into the hot lane fast enough. This makes Blnk promote contended pairs sooner instead of letting them keep slowing down the normal queue. * Only enable immediate rejection when you do not want Blnk to retry lock-contention failures. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Reconciliation Configuration Source: https://docs.blnkfinance.com/advanced/configuration/reconciliations Configure reconciliation strategy, progress updates, and retries in Blnk. This page covers the settings that control how Blnk runs reconciliation jobs and reports progress. *** ## Reconciliation settings Use these settings to configure the default reconciliation strategy, progress updates, and retry behavior. In `blnk.json`, `retry_delay` is integer seconds. The environment variable accepts Go duration format (e.g. `5s`). ```bash blnk.env theme={"system"} BLNK_RECONCILIATION_DEFAULT_STRATEGY=one_to_one BLNK_RECONCILIATION_PROGRESS_INTERVAL=100 BLNK_RECONCILIATION_MAX_RETRIES=3 BLNK_RECONCILIATION_RETRY_DELAY=5s ``` ```json blnk.json theme={"system"} { "reconciliation": { "default_strategy": "one_to_one", "progress_interval": 100, "max_retries": 3, "retry_delay": 5 } } ``` | | Description | Default | | :-------------------------------------- | :------------------------------------------------------------------ | :----------- | | `BLNK_RECONCILIATION_DEFAULT_STRATEGY` | Default matching strategy used for reconciliation. | `one_to_one` | | `BLNK_RECONCILIATION_PROGRESS_INTERVAL` | Number of records Blnk processes before it emits a progress update. | `100` | | `BLNK_RECONCILIATION_MAX_RETRIES` | Maximum number of retry attempts for failed reconciliation work. | `3` | | `BLNK_RECONCILIATION_RETRY_DELAY` | Time Blnk waits before retrying failed reconciliation work. | `5s` | ### `BLNK_RECONCILIATION_DEFAULT_STRATEGY` This sets the reconciliation strategy Blnk uses by default. Use `one_to_one` when one ledger record should match one external record. Change it only when your reconciliation flow regularly needs one-to-many or many-to-one matching. One-to-one, one-to-many, many-to-one matching. ### `BLNK_RECONCILIATION_PROGRESS_INTERVAL` This controls how often Blnk reports progress while a reconciliation job is running. **Tip:** Use a smaller value when you want more frequent progress updates. Use a larger value when you want fewer progress events during large jobs. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Configuration Reference Source: https://docs.blnkfinance.com/advanced/configuration/reference Review required fields and default values for Blnk configuration. This page is a **broad** reference for common Blnk settings and typical defaults. The Blnk `config` package in the application repository is the source of truth; when in doubt, confirm keys and defaults in source. Fields with no defaults are left blank in the examples. In `blnk.json`, most duration fields use integer **seconds** (`lock_duration`, `lock_wait_timeout`, `hot_pair_ttl`). Blnk multiplies them by `time.Second` on load. `queue.backpressure_check_interval` and `typesense.backpressure_retry_interval` use **nanoseconds** in JSON (for example `500000000` for 500ms). Environment variables for all duration fields use Go duration format (for example `500ms`, `5s`). ```bash blnk.env expandable theme={"system"} # Project # Defaults to "Blnk Server" when not set. BLNK_PROJECT_NAME=Blnk Server # Server # Port defaults to 5001. SSL and secure mode are off by default. BLNK_SERVER_SECURE=false BLNK_SERVER_SECRET_KEY= BLNK_SERVER_PORT=5001 BLNK_SERVER_MAX_REQUEST_BODY_SIZE_MB=5 BLNK_SERVER_MAX_UPLOAD_SIZE_MB=256 BLNK_METRICS_BEARER_TOKEN= # Database # DNS is required for Blnk to start successfully. Docker default shown below. # CONNECTION_MAX_LIFETIME and CONNECTION_MAX_IDLE_TIME are integer seconds. BLNK_DATA_SOURCE_DNS=postgres://postgres:password@postgres:5432/blnk?sslmode=disable BLNK_DATABASE_MAX_OPEN_CONNS=50 BLNK_DATABASE_MAX_IDLE_CONNS=25 BLNK_DATABASE_CONN_MAX_LIFETIME=30m BLNK_DATABASE_CONN_MAX_IDLE_TIME=5m # Redis # DNS is required for Blnk to start successfully. Docker default shown below. BLNK_REDIS_DNS=redis:6379 BLNK_REDIS_SKIP_TLS_VERIFY=false # Typesense # Docker default shown for the Typesense endpoint. The API key defaults to "blnk-api-key". BLNK_TYPESENSE_DNS=http://typesense:8108 BLNK_TYPESENSE_KEY=blnk-api-key BLNK_TYPESENSE_BACKPRESSURE_RETRY_INTERVAL=5s # Transactions # LOCK_DURATION and LOCK_WAIT_TIMEOUT are integer seconds. BLNK_TRANSACTION_BATCH_SIZE=1000 BLNK_TRANSACTION_MAX_QUEUE_SIZE=1000 BLNK_TRANSACTION_MAX_WORKERS=10 BLNK_TRANSACTION_LOCK_DURATION=1m BLNK_TRANSACTION_LOCK_WAIT_TIMEOUT=3s BLNK_TRANSACTION_INDEX_QUEUE_PREFIX=transactions BLNK_TRANSACTION_ENABLE_COALESCING=true BLNK_TRANSACTION_ENABLE_QUEUED_CHECKS=false BLNK_TRANSACTION_DISABLE_BATCH_REFERENCE_CHECK=false # Queue BLNK_QUEUE_TRANSACTION=new:transaction BLNK_QUEUE_WEBHOOK=new:webhook BLNK_QUEUE_INDEX=new:index BLNK_QUEUE_INFLIGHT_EXPIRY=new:inflight-expiry BLNK_QUEUE_INFLIGHT_COMMIT=new:inflight-commit BLNK_QUEUE_NUMBER_OF_QUEUES=20 BLNK_QUEUE_INSUFFICIENT_FUND_RETRIES=false BLNK_QUEUE_MAX_RETRY_ATTEMPTS=5 BLNK_QUEUE_MONITORING_PORT=5004 BLNK_QUEUE_WEBHOOK_CONCURRENCY=20 # Hot-lane and worker tuning # HOT_PAIR_TTL is integer seconds. BLNK_QUEUE_ENABLE_HOT_LANE=false BLNK_QUEUE_HOT_QUEUE_NAME=hot_transactions BLNK_QUEUE_HOT_QUEUE_CONCURRENCY=1 BLNK_QUEUE_HOT_PAIR_TTL=300 BLNK_QUEUE_HOT_PAIR_LOCK_CONTENTION_THRESHOLD=3 BLNK_QUEUE_REJECT_LOCK_CONTENTION_IMMEDIATELY=false BLNK_QUEUE_TRANSACTION_WORKER_CONCURRENCY=4 BLNK_QUEUE_ENABLE_BACKPRESSURE=true BLNK_QUEUE_BACKPRESSURE_MEMORY_PERCENT=85 BLNK_QUEUE_BACKPRESSURE_MAX_PENDING_TASKS=0 BLNK_QUEUE_BACKPRESSURE_CHECK_INTERVAL=500ms # Rate limiting # These are the runtime defaults used when rate limiting is not configured. Values are integer seconds. BLNK_RATE_LIMIT_RPS=5000000 BLNK_RATE_LIMIT_BURST=10000000 BLNK_RATE_LIMIT_CLEANUP_INTERVAL_SEC=10800 # Reconciliation # Retry delay uses Go duration format. BLNK_RECONCILIATION_DEFAULT_STRATEGY=one_to_one BLNK_RECONCILIATION_PROGRESS_INTERVAL=100 BLNK_RECONCILIATION_MAX_RETRIES=3 BLNK_RECONCILIATION_RETRY_DELAY=5s # Webhooks # Leave these blank until you are ready to send Slack alerts or webhooks. BLNK_SLACK_WEBHOOK_URL= BLNK_WEBHOOK_URL= BLNK_WEBHOOK_HEADERS={} # Observability BLNK_ENABLE_TELEMETRY=false BLNK_ENABLE_OBSERVABILITY=false # Blnk Cloud DSN for managed monitoring export. Format: https://@/ BLNK_CLOUD_DSN= # Self-hosted OTLP HTTP export (works when observability is enabled) OTEL_EXPORTER_OTLP_TRACES_ENDPOINT= OTEL_EXPORTER_OTLP_METRICS_ENDPOINT= # Tokenization BLNK_TOKENIZATION_SECRET= # Backups BLNK_BACKUP_DIR= BLNK_AWS_ACCESS_KEY_ID= BLNK_AWS_SECRET_ACCESS_KEY= BLNK_S3_ENDPOINT= BLNK_S3_BUCKET_NAME= BLNK_S3_REGION= ``` ```json blnk.json expandable theme={"system"} { "project_name": "Blnk Server", "server": { "secure": false, "secret_key": "", "port": "5001", "metrics_bearer_token": "", "max_request_body_size_mb": 5, "max_upload_size_mb": 256 }, "data_source": { "dns": "postgres://postgres:password@postgres:5432/blnk?sslmode=disable", "max_open_conns": 50, "max_idle_conns": 25, "conn_max_lifetime": 1800, "conn_max_idle_time": 300 }, "redis": { "dns": "redis:6379", "skip_tls_verify": false, "pool_size": 100, "min_idle_conns": 20 }, "typesense": { "dns": "http://typesense:8108", "backpressure_retry_interval": 5000000000 }, "type_sense_key": "blnk-api-key", "transaction": { "batch_size": 1000, "max_queue_size": 1000, "max_workers": 10, "lock_duration": 60, "lock_wait_timeout": 3, "index_queue_prefix": "transactions", "enable_coalescing": true, "enable_queued_checks": false, "disable_batch_reference_check": false }, "queue": { "transaction_queue": "new:transaction", "webhook_queue": "new:webhook", "index_queue": "new:index", "inflight_expiry_queue": "new:inflight-expiry", "inflight_commit_queue": "new:inflight-commit", "number_of_queues": 20, "insufficient_fund_retries": false, "max_retry_attempts": 5, "monitoring_port": "5004", "webhook_concurrency": 20, "enable_hot_lane": false, "hot_queue_name": "hot_transactions", "hot_queue_concurrency": 1, "hot_pair_ttl": 300, "hot_pair_lock_contention_threshold": 3, "reject_lock_contention_immediately": false, "transaction_worker_concurrency": 4, "enable_backpressure": true, "backpressure_memory_percent": 85, "backpressure_max_pending_tasks": 0, "backpressure_check_interval": 500000000 }, "rate_limit": { "requests_per_second": 5000000, "burst": 10000000, "cleanup_interval_sec": 10800 }, "reconciliation": { "default_strategy": "one_to_one", "progress_interval": 100, "max_retries": 3, "retry_delay": 5 }, "notification": { "slack": { "webhook_url": "" }, "webhook": { "url": "", "headers": {} } }, "enable_telemetry": false, "enable_observability": false, "cloud_dsn": "", "tokenization_secret": "", "backup_dir": "", "aws_access_key_id": "", "aws_secret_access_key": "", "s3_endpoint": "", "s3_bucket_name": "", "s3_region": "" } ``` *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Server and Security Configuration Source: https://docs.blnkfinance.com/advanced/configuration/server-security Configure server runtime, secure mode, request size limits, tokenization, and rate limiting in Blnk. This page covers the configuration used to control how the Blnk server listens for requests, protects access to the API, and enables tokenization for sensitive identity fields. *** ## Server settings Use the server settings below to configure how Blnk exposes its API and enforces authentication. ```bash blnk.env theme={"system"} BLNK_SERVER_SECURE=false BLNK_SERVER_SECRET_KEY= BLNK_SERVER_PORT=5001 BLNK_METRICS_BEARER_TOKEN= ``` ```json blnk.json theme={"system"} { "server": { "secure": false, "secret_key": "", "port": "5001", "metrics_bearer_token": "" } } ``` | | Description | Default | | :-------------------------- | :---------------------------------------------------------------------------------------------- | :------ | | `BLNK_SERVER_SECURE` | Enables additional secure-mode protections for the server. | `false` | | `BLNK_SERVER_SECRET_KEY` | Secret used for signing and other cryptographic operations. | None | | `BLNK_SERVER_PORT` | The port Blnk listens on for incoming HTTP requests. | `5001` | | `BLNK_METRICS_BEARER_TOKEN` | Bearer token required to access `/metrics` when set. See [Metrics endpoint](#metrics-endpoint). | None | ### `BLNK_SERVER_SECURE` This controls whether API authentication is enforced when you make requests to the server. When set to `false`, Blnk skips authentication checks. When set to `true`, requests must authenticate via `X-Blnk-Key` header using one of the following: * the master key, `BLNK_SERVER_SECRET_KEY` * a stored API key Scoped keys for day-to-day API access. ### `BLNK_SERVER_SECRET_KEY` This is required for any secure Blnk deployment. It is used in two places in Blnk: 1. As the master API key for authenticated requests to the server 2. As the HMAC signing secret for outgoing webhooks and hook callbacks. Signature verification for webhook consumers. This means the same secret affects both request authentication and webhook signature validation on systems receiving Blnk webhooks. Make sure to keep `BLNK_SERVER_SECRET_KEY` out of version control. Store it in a secret manager or inject it through environment variables in production. ### Best practices 1. Set `BLNK_SERVER_SECURE=true` in any real deployment. 2. Use a strong secret for `BLNK_SERVER_SECRET_KEY`, and make sure to store secrets outside version control. 3. Keep the server secret stable within an environment unless you are prepared to update any systems that verify webhook signatures. ### Metrics endpoint When [monitoring export](/advanced/monitoring) is enabled, Blnk serves Prometheus metrics at `GET /metrics` on the API port and on the [worker monitoring port](/advanced/monitoring-port). Set `metrics_bearer_token` to require `Authorization: Bearer ` on scrape requests. | `server.secure` | Token set | `/metrics` access | | :-------------- | :-------- | :---------------- | | `false` | no | Open | | `false` | yes | Bearer required | | `true` | yes | Bearer required | | `true` | no | Blocked | The same rules apply on the worker monitoring port. Auth failures return structured JSON - see [API error codes](/advanced/error-codes). *** ## Tokenization settings Use tokenization settings to enable encryption and token generation when using the [PII Tokenization](/identities/pii-tokenization) feature. ```bash blnk.env theme={"system"} BLNK_TOKENIZATION_SECRET="blnk-pii-secret" ``` ```json blnk.json theme={"system"} { "tokenization_secret": "blnk-pii-secret" } ``` | | Description | Default | | :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | :------------------ | | `BLNK_TOKENIZATION_SECRET` | Enables tokenization for sensitive identity fields. Must be exactly 32 bytes long. Used as the cryptographic secret for tokenization operations. | Disabled when unset | ### Tokenization behaviour Tokenization is only enabled when `BLNK_TOKENIZATION_SECRET` is set and is exactly 32 bytes long. If the secret is missing, tokenization is disabled. If the secret is set but not 32 bytes long, Blnk does not fail startup, but tokenization operations may fail when called. When enabled, Blnk uses this secret for AES-GCM encryption in standard tokenization and HMAC-based seeding for format-preserving tokenization. **Note:** Store this secret securely and keep it stable for each environment. If you change it after data has already been tokenized, previously tokenized values may no longer detokenize correctly. *** ## Request and payload limits Use these settings to limit request body size and upload size. ```bash blnk.env theme={"system"} BLNK_SERVER_MAX_REQUEST_BODY_SIZE_MB=5 BLNK_SERVER_MAX_UPLOAD_SIZE_MB=256 ``` ```json blnk.json theme={"system"} { "server": { "max_request_body_size_mb": 5, "max_upload_size_mb": 256 } } ``` | | Description | Default | | :------------------------------------- | :------------------------------------------------------------------------------------------------ | :------ | | `BLNK_SERVER_MAX_REQUEST_BODY_SIZE_MB` | Max size in MB for JSON request bodies. | `5` | | `BLNK_SERVER_MAX_UPLOAD_SIZE_MB` | Max size in MB for multipart uploads. Applies to [reconciliation upload](/reference/upload-data). | `256` | ## How limits are applied | Request type | Config key | When exceeded | | :---------------- | :------------------------------------- | :------------------------------------------------------------------------------------------- | | JSON API requests | `BLNK_SERVER_MAX_REQUEST_BODY_SIZE_MB` | `400 Bad Request` with `GEN_MALFORMED_REQUEST` and message `"http: request body too large"`. | | Multipart uploads | `BLNK_SERVER_MAX_UPLOAD_SIZE_MB` | Upload rejected. | These size limits are separate from per-endpoint item limits. A request can be within the item-count limit and still exceed the body-size limit. For example, a bulk transaction request with the accepted count may exceed 5 MB if each transaction contains large metadata or long field values. *** ## Rate limiting Use rate limiting settings to protect the API from abuse and to control traffic spikes more predictably. ```bash blnk.env theme={"system"} BLNK_RATE_LIMIT_RPS=5000000 BLNK_RATE_LIMIT_BURST=10000000 BLNK_RATE_LIMIT_CLEANUP_INTERVAL_SEC=10800 ``` ```json blnk.json theme={"system"} { "rate_limit": { "requests_per_second": 5000000, "burst": 10000000, "cleanup_interval_sec": 10800 } } ``` | | Description | Default | | :------------------------------------- | :-------------------------------------------------------- | :--------- | | `BLNK_RATE_LIMIT_RPS` | Maximum requests allowed per second per client. | `5000000` | | `BLNK_RATE_LIMIT_BURST` | Maximum short burst allowed above the RPS limit. | `10000000` | | `BLNK_RATE_LIMIT_CLEANUP_INTERVAL_SEC` | Cleanup interval for expired rate-limit data, in seconds. | `10800` | ### Rate limiting behaviour If both `BLNK_RATE_LIMIT_RPS` and `BLNK_RATE_LIMIT_BURST` are unset, Blnk applies its built-in defaults. If you set only one of those two values, Blnk derives the other automatically: * if only `BLNK_RATE_LIMIT_RPS` is set, `burst` defaults to `2 * RPS` * if only `BLNK_RATE_LIMIT_BURST` is set, `requests_per_second` defaults to `burst / 2` **Tip:** Start with the defaults unless you have a clear traffic policy or abuse-prevention requirement. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Transaction Configuration Source: https://docs.blnkfinance.com/advanced/configuration/transactions Configure transaction processing, batching, locking, and coalescing in Blnk. This page covers the settings that control how Blnk executes, batches, and locks transactions. For queue names, sharding, retries, and hot-lane routing, see [Queue configuration](/advanced/configuration/queue). *** ## Transaction settings Use these settings to control transaction batching, worker behavior, and queued validation. ```bash blnk.env theme={"system"} BLNK_TRANSACTION_MAX_QUEUE_SIZE=1000 BLNK_TRANSACTION_MAX_WORKERS=10 BLNK_TRANSACTION_INDEX_QUEUE_PREFIX=transactions BLNK_TRANSACTION_ENABLE_QUEUED_CHECKS=false ``` ```json blnk.json theme={"system"} { "transaction": { "batch_size": 1000, "max_queue_size": 1000, "max_workers": 10, "index_queue_prefix": "transactions", "enable_queued_checks": false } } ``` | Variable | Default | Description | | --------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `BLNK_TRANSACTION_MAX_QUEUE_SIZE` | `1000` | In-memory **channel buffer size** for the batch worker used by `ProcessTransactionInBatches` (not a Redis queue depth limit). | | `BLNK_TRANSACTION_MAX_WORKERS` | `10` | Concurrency for **batch and reconciliation** transaction workers. For the main Redis-backed **queued** transaction pool, use `BLNK_QUEUE_TRANSACTION_WORKER_CONCURRENCY` and [hot-lane settings](/advanced/configuration/queue#hot-lane-routing-settings) instead. | | `BLNK_TRANSACTION_INDEX_QUEUE_PREFIX` | `transactions` | **Collection / index name prefix** for **transaction search** documents (e.g. Typesense), not a Redis transaction queue name. | | `BLNK_TRANSACTION_ENABLE_QUEUED_CHECKS` | `false` | Includes queued debits when loading balances for pre-transaction validation. | *** ## Coalescing settings Use Coalescing settings to improve performance and throughput under load when working with [hot balances](/guides/hot-balances#coalescing). ```bash blnk.env theme={"system"} BLNK_TRANSACTION_BATCH_SIZE=1000 BLNK_TRANSACTION_ENABLE_COALESCING=true BLNK_TRANSACTION_DISABLE_BATCH_REFERENCE_CHECK=false ``` ```json blnk.json theme={"system"} { "transaction": { "batch_size": 1000, "enable_coalescing": true, "disable_batch_reference_check": false } } ``` | Variable | Default | Description | | ------------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BLNK_TRANSACTION_BATCH_SIZE` | `1000` | Maximum number of compatible queued transactions Blnk tries to process in one coalesced batch. | | `BLNK_TRANSACTION_ENABLE_COALESCING` | `true` | Enables coalesced execution for normal queued transaction processing. | | `BLNK_TRANSACTION_DISABLE_BATCH_REFERENCE_CHECK` | `false` | Skips the bulk reference lookup during coalesced batch processing. References are still validated per transaction. When `false`, the bulk check stays enabled. | ### `BLNK_TRANSACTION_ENABLE_COALESCING` When Coalescing is enabled, Blnk identifies the queued transactions based on if they share the same source, destination, and currency, batches them in-memory, and applies them in a single commit. This works best when queued traffic arrives in bursts and many transactions overlap on the same balances. Higher throughput on contended balances. ### `BLNK_TRANSACTION_DISABLE_BATCH_REFERENCE_CHECK` During coalesced batch processing, Blnk can look up all transaction references in the batch with one bulk query before commit. That prefetch lets validation compare each reference against known usage without a separate lookup per transaction. Set this to `true` to turn off that bulk lookup. Reference validation does not stop. With no prefetched set, Blnk falls back to validating each transaction individually. The default (`false`) keeps the bulk check enabled. Set `true` only when you want to trade the bulk lookup for per-transaction reference checks during coalesced execution. *** ## Lock settings Use these settings to control [distributed balance locks](/guides/concurrency#distributed-redis-balance-locks) for direct and queued transaction processing, including coalesced batch execution. ```bash blnk.env theme={"system"} BLNK_TRANSACTION_LOCK_DURATION=1m BLNK_TRANSACTION_LOCK_WAIT_TIMEOUT=3s ``` ```json blnk.json theme={"system"} { "transaction": { "lock_duration": 60, "lock_wait_timeout": 3 } } ``` | Variable | Default | Description | | ------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BLNK_TRANSACTION_LOCK_DURATION` | `60` | How long a distributed balance lock can live once acquired, in **seconds** (non-zero values). | | `BLNK_TRANSACTION_LOCK_WAIT_TIMEOUT` | `3` | How long Blnk waits when acquiring **transaction balance locks** (shared path for direct and queued processing, including coalesced batch execution), in **seconds**. | ### `BLNK_TRANSACTION_LOCK_WAIT_TIMEOUT` This controls how long a transaction should wait when [acquiring a lock](/guides/concurrency#distributed-redis-balance-locks) before the transaction fails with a lock error. If locks are not acquired in time, the **operation fails with a lock error**. For a **direct** (`skip_queue=true`) request, that is typically returned to the client. For **queued** work, workers may retry or reject depending on [queue settings](/advanced/configuration/queue#queue-settings), e.g. `BLNK_QUEUE_REJECT_LOCK_CONTENTION_IMMEDIATELY` and `BLNK_QUEUE_MAX_RETRY_ATTEMPTS`. **Tip:** Keep the default value unless transactions regularly outlive the lock window. This config is especially important for transactions that bypass the queue. *** ## Hash chain settings Available in version 0.15.0 and later. The optional [transaction hash chain](/transactions/hash) seals applied transactions into a global tamper-evident sequence. It is **disabled by default**. When enabled, a background processor backfills unchained rows and appends new transactions after a trailing delay. Run `blnk verify-chain` from your Core deployment to check chain integrity. In `blnk.json`, `poll_interval` and `trailing_delay` are **integer seconds**. Environment variables accept Go duration format (for example `5s`, `30s`). ```bash blnk.env theme={"system"} BLNK_TRANSACTION_HASHCHAIN_ENABLED=false BLNK_TRANSACTION_HASHCHAIN_POLL_INTERVAL=5s BLNK_TRANSACTION_HASHCHAIN_BATCH_SIZE=1000 BLNK_TRANSACTION_HASHCHAIN_TRAILING_DELAY=30s ``` ```json blnk.json theme={"system"} { "transaction": { "hash_chain": { "enabled": false, "poll_interval": 5, "batch_size": 1000, "trailing_delay": 30 } } } ``` | Variable | Default | Description | | ------------------------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- | | `BLNK_TRANSACTION_HASHCHAIN_ENABLED` | `false` | Enables the background hash-chain processor. | | `BLNK_TRANSACTION_HASHCHAIN_POLL_INTERVAL` | `5s` / `5` | How often the processor polls for unchained transactions. Env: Go duration (for example `5s`). JSON: seconds. | | `BLNK_TRANSACTION_HASHCHAIN_BATCH_SIZE` | `1000` | Maximum transactions sealed per processor batch. | | `BLNK_TRANSACTION_HASHCHAIN_TRAILING_DELAY` | `30s` / `30` | Wait time before sealing the newest transactions, so in-flight writes can settle. Env: Go duration (for example `30s`). JSON: seconds. | Per-row hashes, chain verification, and sealed fields. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Webhook Configuration Source: https://docs.blnkfinance.com/advanced/configuration/webhooks Configure Slack webhooks and outgoing HTTP webhooks for Blnk alerts and events. This page covers the settings that control how Blnk sends outbound notifications to Slack and to your own HTTP endpoints. *** ## Webhook settings Use the `notification` object to configure Slack incoming webhooks and a generic application webhook. You can set either integration alone, or both at the same time. ```bash blnk.env wrap theme={"system"} BLNK_SLACK_WEBHOOK_URL= BLNK_WEBHOOK_URL= BLNK_WEBHOOK_HEADERS={"Authorization":"Bearer ","Content-Type":"application/json"} ``` ```json blnk.json theme={"system"} { "notification": { "slack": { "webhook_url": "" }, "webhook": { "url": "", "headers": { "Authorization": "Bearer ", "Content-Type": "application/json" } } } } ``` | | Description | Default | | :----------------------- | :-------------------------------------------------------------------------- | :------ | | `BLNK_SLACK_WEBHOOK_URL` | Slack incoming webhook URL used for team alerts. | None | | `BLNK_WEBHOOK_URL` | HTTPS endpoint that receives Blnk event notifications. | None | | `BLNK_WEBHOOK_HEADERS` | Extra HTTP headers sent with webhook requests (for example authentication). | `{}` | If using `.env`, set `BLNK_WEBHOOK_HEADERS` to a JSON-encoded object. For `blnk.json`, set `notification.webhook.headers` to a JSON object. ### `BLNK_SLACK_WEBHOOK_URL` When set, Blnk can post messages to the Slack channel associated with the incoming webhook. To create a webhook URL, follow Slack's guide: [Sending messages using incoming webhooks](https://api.slack.com/messaging/webhooks). Treat Slack webhook URLs like secrets. Anyone with the URL can post to your channel, so store them in a secret manager or environment variables, not in version control. ### `BLNK_WEBHOOK_URL` When set, Blnk delivers notifications to your application at this URL. Use this when you want to trigger workflows, sync internal services, or custom alerting outside of your ledger. ### `BLNK_WEBHOOK_HEADERS` Optional extra HTTP headers Blnk sends with each global webhook request. Use this for authentication tokens, custom `Content-Type` values, or other gateway requirements. These headers are separate from Blnk's signing headers (`X-Blnk-Signature` and `X-Blnk-Timestamp`). See [Webhook security](/webhooks/overview#webhook-security) for signature verification. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # API Error Codes Source: https://docs.blnkfinance.com/advanced/error-codes Structured API error responses, error codes, and HTTP status reference for Blnk Core 0.15.0 and later. Available from Blnk Core 0.15.0. Blnk returns a stable `error_detail.code` on API errors. Use this code for programmatic handling. Treat `error`, `errors`, and `error_detail.message` as display text only. *** ## Error response shape Error responses include `error_detail` and `error` (or `errors` on some list and filter endpoints): ```json Error response theme={"system"} { "error": "transaction not found", "error_detail": { "code": "TXN_NOT_FOUND", "message": "transaction not found", "details": {} } } ``` | Field | Description | | ---------------------- | ---------------------------------------------------------------------------------------------------- | | `error_detail.code` | Stable code from the catalog below. Branch on this field, not on message text or `error` / `errors`. | | `error_detail.message` | Human-readable message. May change between releases. | | `error_detail.details` | Optional structured context (filter parse errors, reindex progress, batch IDs, and similar). | | `error` or `errors` | Exist for backwards compatibility and display. | Unclassified server failures return HTTP `500` with the sanitized message `"internal server error"`. The underlying error is logged server-side and is not echoed to clients. *** ## How to handle errors Handle Blnk API errors in two steps: 1. Use the HTTP status code to understand the type of failure. 2. Use `error_detail.code` to decide what your application should do. ```js Recommended flow wrap expandable theme={"system"} const body = await response.json(); if (!response.ok) { const code = body.error_detail?.code; switch (code) { case "TXN_NOT_FOUND": // Show a not found state or retry with a valid transaction ID. break; case "TXN_DUPLICATE_REFERENCE": // Treat as a conflict. You may fetch the existing transaction by reference. break; case "TXN_INSUFFICIENT_FUNDS": // Ask the user to fund the source balance or choose another source. break; default: // Fall back to status-based handling. break; } } ``` When debugging, log: * HTTP status `code` * `error_detail.code` * `error_detail.details` when present These fields give you the clearest context without depending on message text. Do not build logic around `error`, `errors`, or `error_detail.message`. These fields are meant for display and may change between releases. *** ## Error-code catalog Codes are domain-prefixed. Each code has one default HTTP status. ### Platform Errors that apply across endpoints: malformed requests, validation failures, rate limits, lock contention, and unexpected server failures. Codes use the `GEN_*` prefix. | Code | HTTP | Meaning | | ----------------------- | ---- | ------------------------------------------------------------------------------- | | `GEN_MALFORMED_REQUEST` | 400 | Request body could not be parsed (invalid JSON, wrong types, or body too large) | | `GEN_VALIDATION_ERROR` | 400 | Request failed validation (invalid params, filters, fields) | | `GEN_MISSING_PARAMETER` | 400 | A required route or query parameter is missing | | `GEN_BAD_REQUEST` | 400 | Request rejected; no more specific code applies | | `GEN_NOT_FOUND` | 404 | Resource not found; no domain-specific code applies | | `GEN_CONFLICT` | 409 | Request conflicts with current resource state | | `GEN_RESOURCE_LOCKED` | 423 | A concurrent operation holds the lock; retry shortly | | `GEN_RATE_LIMITED` | 429 | Too many requests | | `GEN_INTERNAL` | 500 | Unexpected server failure; message is sanitized | Errors from API key authentication, authorization scopes, and metrics bearer tokens. Codes use the `AUTH_*` prefix. | Code | HTTP | Meaning | | ------------------------------- | ---- | ------------------------------------------------------------ | | `AUTH_MISSING_API_KEY` | 401 | No `X-Blnk-Key` header supplied | | `AUTH_INVALID_API_KEY` | 401 | API key is unknown | | `AUTH_EXPIRED_API_KEY` | 401 | API key is expired or revoked | | `AUTH_MISSING_PRINCIPAL` | 401 | Authenticated API key principal missing from request context | | `AUTH_INSUFFICIENT_PERMISSIONS` | 403 | API key lacks the required `resource:action` scope | | `AUTH_UNKNOWN_RESOURCE` | 403 | Request path maps to no known resource | | `AUTH_MASTER_KEY_REQUIRED` | 403 | Endpoint (hooks management) requires the master key | | `AUTH_CROSS_OWNER_ACCESS` | 403 | Attempt to manage another owner's API keys | | `AUTH_SCOPE_ESCALATION` | 403 | Attempt to grant scopes broader than the caller's | | `AUTH_METRICS_TOKEN_REQUIRED` | 401 | `/metrics` requires `Authorization: Bearer ` | | `AUTH_INVALID_BEARER_TOKEN` | 401 | `/metrics` bearer token mismatch | | `AUTH_METRICS_DISABLED` | 403 | Secure mode on but no metrics bearer token configured | Errors when creating, listing, or validating API keys. Codes use the `APIKEY_*` prefix. | Code | HTTP | Meaning | | ----------------------- | ---- | ---------------------------------------------------------------- | | `APIKEY_NOT_FOUND` | 404 | API key does not exist | | `APIKEY_OWNER_REQUIRED` | 400 | `owner` is required when creating or listing with the master key | | `APIKEY_INVALID` | 400 | API key payload validation failure | ### Core resources Errors when creating or fetching ledgers. Codes use the `LGR_*` prefix. | Code | HTTP | Meaning | | --------------- | ---- | ------------------------------------------ | | `LGR_NOT_FOUND` | 404 | Ledger does not exist | | `LGR_DUPLICATE` | 409 | Ledger with this name or ID already exists | Errors for balance lookups, historical snapshots, monitors, and validation. Codes use the `BAL_*` prefix. | Code | HTTP | Meaning | | ----------------------- | ---- | ---------------------------------------------------- | | `BAL_NOT_FOUND` | 404 | Balance does not exist | | `BAL_HISTORY_NOT_FOUND` | 404 | No balance snapshot or history at the requested time | | `BAL_INVALID_TIMESTAMP` | 400 | Timestamp must be ISO 8601 | | `BAL_VALIDATION_ERROR` | 400 | Balance (or linked identity) validation failure | | `BAL_MONITOR_NOT_FOUND` | 404 | Balance monitor does not exist | Errors when recording, validating, committing, voiding, or bulk-processing transactions. Codes use the `TXN_*` prefix. | Code | HTTP | Meaning | | ---------------------------- | ---- | ------------------------------------------------------------------- | | `TXN_NOT_FOUND` | 404 | Transaction, refundable transaction, or queued source was not found | | `TXN_INSUFFICIENT_FUNDS` | 400 | Source balance cannot cover the transaction | | `TXN_INVALID_AMOUNT` | 400 | Transaction amount must be positive | | `TXN_PRECISION_NOT_INTEGER` | 400 | `precision` must be an integer value | | `TXN_INVALID_DISTRIBUTION` | 400 | Multi-source or destination distribution is invalid | | `TXN_DUPLICATE_REFERENCE` | 409 | The `reference` has already been used | | `TXN_NOT_INFLIGHT` | 400 | Transaction is not in `INFLIGHT` status | | `TXN_ALREADY_COMMITTED` | 409 | Inflight transaction was already committed | | `TXN_ALREADY_VOIDED` | 409 | Inflight transaction was already voided | | `TXN_COMMIT_AMOUNT_EXCEEDED` | 400 | Commit exceeds the original or remaining inflight amount | | `TXN_INVALID_STATUS_ACTION` | 400 | Inflight update status must be `commit` or `void` | | `TXN_BULK_EMPTY` | 400 | Bulk payload contains no transactions or IDs | | `TXN_BULK_LIMIT_EXCEEDED` | 400 | Bulk payload exceeds the per-request item limit | | `TXN_VALIDATION_ERROR` | 400 | Transaction payload failed validation | Errors for identity records and field tokenization. Codes use the `IDT_*` prefix. | Code | HTTP | Meaning | | ----------------------------- | ---- | ------------------------------------------------- | | `IDT_NOT_FOUND` | 404 | Identity does not exist | | `IDT_VALIDATION_ERROR` | 400 | Identity payload validation failure | | `IDT_FIELD_NOT_TOKENIZABLE` | 400 | Field is not in the tokenizable set | | `IDT_FIELD_ALREADY_TOKENIZED` | 409 | Field is already tokenized | | `IDT_FIELD_NOT_TOKENIZED` | 400 | Field is not tokenized and cannot be detokenized. | | `IDT_FIELD_NOT_FOUND` | 400 | Field does not exist on the identity | | `IDT_TOKENIZATION_DISABLED` | 403 | Tokenization is not configured on this server | Errors for reconciliation runs, matching rules, and external data uploads. Codes use the `RECON_*` prefix. | Code | HTTP | Meaning | | -------------------------------- | ---- | --------------------------------------------------------------------------------------------------- | | `RECON_NOT_FOUND` | 404 | Reconciliation run does not exist | | `RECON_RULE_NOT_FOUND` | 404 | Matching rule does not exist | | `RECON_UPLOAD_FAILED` | 400 | External data file upload failed because of the request. | | `RECON_UPLOAD_PROCESSING_FAILED` | 500 | Server failed to process the uploaded file | | `RECON_RULE_INVALID` | 400 | Matching rule failed validation. This can include name, criteria, operator, field, or drift errors. | | `RECON_MATCHING_RULES_REQUIRED` | 400 | `matching_rule_ids` is required | | `RECON_EXTERNAL_TXNS_REQUIRED` | 400 | `external_transactions` is required | | `RECON_START_FAILED` | 500 | Reconciliation could not be started | ### Other operations Errors when updating metadata on ledgers, balances, transactions, and other entities. Codes use the `META_*` prefix. | Code | HTTP | Meaning | | ------------------------- | ---- | ----------------------------------------- | | `META_ENTITY_NOT_FOUND` | 404 | Entity for metadata update does not exist | | `META_UNSUPPORTED_ENTITY` | 400 | Entity type does not support metadata | | `META_INVALID_ENTITY_ID` | 400 | Entity ID is missing or malformed | Errors for transaction hook registration and management. Codes use the `HOOK_*` prefix. | Code | HTTP | Meaning | | ----------------------- | ---- | ----------------------------------------------------------------- | | `HOOK_NOT_FOUND` | 404 | Hook does not exist | | `HOOK_INVALID` | 400 | Hook payload validation failure | | `HOOK_OPERATION_FAILED` | 500 | Hook registration, update, list, or delete infrastructure failure | Errors from queued async processing. Codes use the `QUEUE_*` prefix. | Code | HTTP | Meaning | | -------------------- | ---- | -------------------------------------------------------------------------------------- | | `QUEUE_BACKPRESSURE` | 503 | Enqueue rejected because Redis memory or pending task count exceeded configured limits | Errors from search queries and Typesense reindex jobs. Codes use the `SRCH_*` prefix. | Code | HTTP | Meaning | | -------------------------- | ---- | ------------------------------------------------------------------------ | | `SRCH_QUERY_INVALID` | 400 | Search payload is invalid | | `SRCH_FAILED` | 500 | Search backend failure | | `SRCH_REINDEX_IN_PROGRESS` | 409 | A reindex is already running. `details` carries progress when available. | | `SRCH_REINDEX_NOT_STARTED` | 404 | No reindex has been started | Errors from admin operations such as database backup. Codes use the `ADMIN_*` prefix. | Code | HTTP | Meaning | | --------------------- | ---- | ---------------------- | | `ADMIN_BACKUP_FAILED` | 500 | Database backup failed | ### Bulk commit and void Bulk commit and void have two types of failures: 1. Request-level failures 2. Per-item failures Request-level failures reject the whole request, i.e. they reject the whole call before any item runs. Per-item failures are returned inside the bulk result. Request-level failures reject the whole request, i.e. they reject the whole call before any item runs. Per-item failures are returned inside the bulk result. The response applies the standard transaction error shape with codes such as `TXN_BULK_EMPTY` or `TXN_BULK_LIMIT_EXCEEDED`. ```json Empty batch theme={"system"} { "error": "transactions cannot be empty", "error_detail": { "code": "TXN_BULK_EMPTY", "message": "transactions cannot be empty" } } ``` Bulk commit expects a `transactions` array. Bulk void expects `transaction_ids`. Sending the wrong shape returns `TXN_BULK_EMPTY` because unrecognized fields are dropped. Valid requests return HTTP `200` with `succeeded`, `failed`, and `results`. Branch on `results[].code`, not the HTTP status. Per-item codes are specific to bulk processing and are not part of the `TXN_*` catalog. | Code | Meaning | | ------------------- | ------------------------------------------------------- | | `NOT_FOUND` | Transaction ID does not exist | | `ALREADY_COMMITTED` | Inflight transaction was already committed | | `ALREADY_VOIDED` | Inflight transaction was already voided | | `NOT_INFLIGHT` | Transaction is not in `INFLIGHT` status | | `INVALID_AMOUNT` | Commit amount is invalid | | `LOCKED` | Concurrent operation on the same transaction | | `INTERNAL_ERROR` | Unexpected server failure for this item | | `QUEUED` | Item was queued for processing | | `ALREADY_QUEUED` | A commit or void is already queued for this transaction | ```json Mixed batch (skip_queue: true) wrap theme={"system"} { "succeeded": 1, "failed": 2, "results": [ { "transaction_id": "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", "status": "succeeded" }, { "transaction_id": "txn_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30", "status": "failed", "code": "NOT_FOUND", "message": "Transaction with ID 'txn_c5d9e2a1-7b4f-4a3c-9e8d-1f6a2b4c8d30' not found" }, { "transaction_id": "txn_7c91e4b2-3d8a-4f6e-b1c2-9a8d7e6f5b4a", "status": "failed", "code": "ALREADY_COMMITTED", "message": "Transaction is already committed" } ] } ``` Loop through `results` and handle failed items by code. Queued items use `status: "queued"` and do not need the same error paths. ```js Recommended flow wrap expandable theme={"system"} const { results } = await response.json(); for (const item of results) { if (item.status === "succeeded") { continue; } if (item.status === "queued") { // Item was queued for processing. // No error handling is needed unless your application treats queued items separately. continue; } if (item.status === "failed") { switch (item.code) { case "NOT_FOUND": // Confirm the transaction ID. break; case "ALREADY_COMMITTED": case "ALREADY_VOIDED": // Treat as already processed. break; case "LOCKED": // Retry shortly with backoff. break; default: // Handle NOT_INFLIGHT, INVALID_AMOUNT, INTERNAL_ERROR, and other failures. break; } } } ``` *** ## Legacy codes Blnk normalizes older generic error names to canonical `error_detail.code` values before the response leaves the server. You will not receive legacy error names in `error_detail.code`. To handle legacy behaviour: * Use `error_detail.code` from the catalog above. * Prefer domain-specific codes over generic codes. For example, a missing transaction returns `TXN_NOT_FOUND`, not `GEN_NOT_FOUND`. * Do not parse `error`, `errors`, or `error_detail.message` to infer the code. | Legacy internal code | Canonical `error_detail.code` | | ----------------------- | ----------------------------- | | `NOT_FOUND` | `GEN_NOT_FOUND` | | `CONFLICT` | `GEN_CONFLICT` | | `BAD_REQUEST` | `GEN_BAD_REQUEST` | | `INVALID_INPUT` | `GEN_VALIDATION_ERROR` | | `INTERNAL_SERVER_ERROR` | `GEN_INTERNAL` | | `RATE_LIMITED` | `GEN_RATE_LIMITED` | *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Load Testing Source: https://docs.blnkfinance.com/advanced/load-testing Learn to perform load testing on your Blnk server using k6. Use load testing to ensure that your Blnk deployment can handle expected traffic and maintain performance under various conditions. This is a critical step in setting up your Blnk server. Blnk includes a comprehensive load testing suite built with [k6](https://k6.io/), a modern load testing tool. This guide walks you through finding, configuring, and running load tests to validate your Blnk server's performance across different scenarios. *** ## Before you start Make sure you have: 1. Cloned the Blnk repository to your local machine 2. [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) installed, for running the Blnk server 3. k6 installed on your machine. Visit [k6.io](https://k6.io/) for installation instructions 4. Your Blnk server running and accessible If you haven't deployed Blnk yet, follow the [installation guide](/home/install) first. *** ## Getting started You can find the load test files in the `tests/loadtest` directory of the Blnk repository. Navigate to this directory to access all available test scripts: ```bash theme={"system"} cd tests/loadtest ``` *** ## Main test script The primary load test script (`script.js`) supports five configurable load-testing scenarios, each controlled via environment variables. Each scenario targets a specific aspect of your Blnk server’s transaction performance, allowing you to test different load patterns and system behaviors. * **Baseline**: Steady, constant load to measure normal performance. * **Ramp**: Gradually increasing load to find when your server struggles. * **Contention**: Tests when one balance gets most of the traffic. * **Soak**: Long-duration test (2 hours) to find memory leaks and stability issues. * **Spike**: Sudden traffic surge to test recovery behavior. By default, tests target `http://localhost:5001`. Ensure your Blnk server is running and accessible at this address, or configure the URL using the `URL` environment variable. Use the `SCENARIO` environment variable to select which scenario to run. Each scenario has specific configuration options: Runs a steady, constant load to measure your server's normal performance. Use this to see how fast your server responds under normal conditions. ```bash wrap tests/loadtest theme={"system"} k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=baseline --env VUS=100 --env DURATION=5m script.js ``` Number of virtual users to run concurrently Test duration (e.g., "5m", "30s", "1h") Slowly increases traffic over time to find when your server starts to struggle. Starts with 100 users and gradually increases to 1000 users across three stages. ```bash wrap theme={"system"} k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=ramp \ --env START_VUS=100 \ --env VUS1=300 --env STAGE1=5m \ --env VUS2=600 --env STAGE2=5m \ --env VUS3=1000 --env STAGE3=5m \ script.js ``` Initial number of virtual users at the start of the test Target number of virtual users for the first ramp stage Duration of the first ramp stage Target number of virtual users for the second ramp stage Duration of the second ramp stage Target number of virtual users for the third ramp stage Duration of the third ramp stage Tests what happens when one balance gets most of the traffic while others get less. This simulates real-world scenarios where some accounts are much busier than others. ```bash wrap tests/loadtest theme={"system"} k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=contention \ --env HOT_DEST=@hot-balance-0001 \ --env HOT_RPS=308 \ --env RANDOM_RPS=132 \ --env DURATION=5m \ --env VUS=200 \ --env MAX_VUS=800 \ script.js ``` The balance identifier that receives concentrated traffic (70% of requests) Requests per second targeting the hot balance Requests per second targeting random balances Test duration (e.g., "5m", "30s", "1h") Number of pre-allocated virtual users Maximum number of virtual users that can be allocated Runs a steady load for a long time (default: 2 hours) to check if your server has memory leaks or other problems that only show up after running for a while. ```bash wrap tests/loadtest theme={"system"} k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=soak --env VUS=200 --env DURATION=2h script.js ``` Number of virtual users to run concurrently Test duration (e.g., "5m", "30s", "1h", "2h") Soak tests run for extended periods. Monitor your server's resource usage during these tests. Simulates a sudden traffic spike to see if your server can handle the surge and recover afterward. ```bash wrap tests/loadtest theme={"system"} k6 run --env URL=http://localhost:5001/transactions --env SCENARIO=spike \ --env BASE_RPS=200 \ --env SPIKE_RPS=600 \ --env WARM=2m \ --env PEAK=1m \ --env COOL=3m \ --env PRE_VUS=300 \ --env MAX_VUS=1200 \ script.js ``` Baseline requests per second during warm-up and recovery phases Peak requests per second during spike phase Duration of warm-up phase before spike Duration of spike phase Duration of recovery phase after spike Number of pre-allocated virtual users Maximum number of virtual users that can be allocated during the spike *** ## Understanding test results **k6** provides comprehensive metrics for each test run. Key metrics to monitor include: * **http\_req\_duration**: Request latency (p50, p95, p99 percentiles) * **http\_req\_failed**: Error rate (should be \< 0.1%) * **http\_reqs**: Total requests processed * **vus**: Virtual users active during the test The main test script (`script.js`) includes built-in performance thresholds that automatically fail tests if exceeded: * **Error rate**: Less than 0.1% (`http_req_failed < 0.001`) * **P95 latency**: Less than 300ms * **P99 latency**: Less than 600ms The main test script automatically exports results to `summary.json`. Run any scenario: ```bash wrap theme={"system"} k6 run --env SCENARIO=baseline script.js ``` Results are automatically saved to `summary.json` in the current directory. The test suite includes a utility script to convert k6 JSON output into transactions per minute (TPM) CSV format. First, export k6 results as JSON: ```bash wrap theme={"system"} k6 run --out json=results.json --env SCENARIO=baseline script.js ``` Then convert to CSV: ```bash wrap theme={"system"} python3 tools/k6_json_to_tpm.py results.json tpm_results.csv ``` Path to k6 JSON output file (use `--out json=output.json` when running k6) Path to output CSV file The generated CSV includes: minute timestamp, requests, transactions per minute (tpm), success ratio, p50/p95/p99 latency, and apdex score. *** ## Troubleshooting ### Connection refused errors If you see connection errors, verify: 1. Your Blnk server is running and accessible. 2. The URL matches your server's address and port. 3. Firewall rules allow connections to the Blnk server. ### High error rates If error rates exceed thresholds: 1. Check Blnk server logs for errors. 2. Verify database connection pool settings. 3. Review server resource usage (CPU, memory). 4. Consider reducing virtual users or request rate. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Metrics reference Source: https://docs.blnkfinance.com/advanced/metrics-reference Metric names, types, and attributes for Blnk Core. Blnk emits metrics as numeric time-series measurements from the server and workers: transaction throughput, queue depth, inflight lifecycle events, worker retries, and related operational signals. This page catalogs every metric sent by Blnk: name, instrument type, attributes, and allowed values. For setup and export modes, see [Monitoring in Blnk](/advanced/monitoring). *** ## Metrics Track ledger writes from acceptance through final status. Use these metrics for throughput, latency SLOs, and rejection spikes by reason or currency. | Name | Type | Attributes | Description | | :---------------------------------- | :-------- | :------------------- | :-------------------------------------------------- | | `blnk_transaction_total` | Counter | `status`, `currency` | Total transactions by final status and currency | | `blnk_transaction_duration_seconds` | Histogram | `status` | Wall-clock time of `RecordTransaction()` in seconds | | `blnk_transaction_rejected_total` | Counter | `reason` | Rejected transactions by reason | Allowed values for each attribute label: | Attribute | Values | | :--------- | :------------------------------------------------------------------------------------- | | `status` | `APPLIED` · `REJECTED` · `INFLIGHT` · `VOID` · `COMMIT` | | `reason` | `insufficient_funds` · `overdraft_limit` · `lock_contention` · `max_retries` · `other` | | `currency` | ISO currency code from the transaction (for example `USD`, `NGN`, `EUR`) | Measure async processing on workers after a transaction is enqueued. Use enqueue rate to spot API-to-worker inflow. Use processing duration to detect worker slowdown or backlog buildup. | Name | Type | Attributes | Description | | :--------------------------------------- | :-------- | :----------- | :------------------------------------------------ | | `blnk_queue_enqueued_total` | Counter | `queue_name` | Transactions enqueued for async processing | | `blnk_queue_processing_duration_seconds` | Histogram | `result` | Time spent processing a transaction in the worker | Allowed values for each attribute label: | Attribute | Values | | :----------- | :----------------------------------------------------------------------------------------- | | `queue_name` | `new:transaction_1` … `new:transaction_N` (sharded queues) · `hot_transactions` (hot lane) | | `result` | `success` | Count balance creation over time. Useful for growth tracking and correlating new account activity with transaction volume. | Name | Type | Attributes | Description | | :--------------------------- | :------ | :--------- | :--------------------- | | `blnk_balance_created_total` | Counter | - | Total balances created | Count two-phase inflight lifecycle events: commits that finalize a hold and voids that release it without applying. Use these to monitor inflight resolution volume, not inflight creation (see the **Transactions** tab). | Name | Type | Attributes | Description | | :--------------------------- | :------ | :--------- | :------------------------------ | | `blnk_inflight_commit_total` | Counter | - | Inflight transactions committed | | `blnk_inflight_void_total` | Counter | - | Inflight transactions voided | Track how the worker groups queued transactions into batches for bulk processing. Batch size shows coalescing efficiency; outcome counters show success, failure, and skipped attempts. | Name | Type | Attributes | Description | | :----------------------------- | :-------- | :--------- | :------------------------------- | | `blnk_transaction_batch_size` | Histogram | - | Transactions per coalesced batch | | `blnk_transaction_batch_total` | Counter | `result` | Coalescing attempts by outcome | Allowed values for each attribute label: | Attribute | Values | | :-------- | :-------------------------------- | | `result` | `success` · `failure` · `skipped` | Monitor contention on frequently updated balance pairs. Contention counters rise when lock acquisition fails; lane routing shows how much traffic goes to the dedicated hot queue versus normal shards. | Name | Type | Attributes | Description | | :-------------------------------- | :------ | :--------- | :--------------------------------- | | `blnk_hotpairs_contention_total` | Counter | - | Lock acquisition failures | | `blnk_hotpairs_lane_routed_total` | Counter | `lane` | Transactions routed to queue lanes | Allowed values for each attribute label: | Attribute | Values | | :-------- | :--------------- | | `lane` | `normal` · `hot` | Count worker retry events when async processing fails and the job is re-attempted. Sustained retries often point to transient failures or lock contention worth correlating with queue and hot-pair metrics. | Name | Type | Attributes | Description | | :-------------------------- | :------ | :--------- | :------------------ | | `blnk_worker_retries_total` | Counter | `reason` | Worker retry events | Allowed values for each attribute label: | Attribute | Values | | :-------- | :----------------------------- | | `reason` | `insufficient_funds` · `other` | Gauge hash-chain sealing progress when [transaction hash chain](/transactions/hash) is enabled. Backlog and lag show unsealed transactions and processing delay; head sequence tracks the current chain tip. | Name | Type | Attributes | Description | | :----------------------- | :---- | :--------- | :---------------------------------------------- | | `blnk_chain_backlog` | Gauge | - | Transactions not yet sealed into the hash chain | | `blnk_chain_head_seq` | Gauge | - | Sequence number of the hash-chain head | | `blnk_chain_lag_seconds` | Gauge | - | Seconds since the hash chain last advanced | *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Monitoring in Blnk Source: https://docs.blnkfinance.com/advanced/monitoring Monitor and debug Blnk Core with logs, traces, and metrics in Blnk Cloud or your own stack. Running a ledger means caring about correctness, latency, and throughput. Monitoring shows whether Blnk is healthy and where problems show up, without inferring system state from API responses alone. This guide helps you set up monitoring for your Core. For configuration flags, see [Observability configuration](/advanced/configuration/observability). *** ## Quick setup: Blnk Cloud Requires Blnk Core 0.14.4 or later. When something breaks in your production ledger, you need to see what happened: which request failed, where time was spent, etc. Waiting on another team for logs slows that down and creates bottlenecks. Blnk Cloud monitoring workspace showing log lines from the server and worker Blnk Cloud includes built-in monitoring for your ledger. No Jaeger or Prometheus setup required. Simply connect your Core instance via a Blnk Cloud DSN and you can start viewing logs, traces, and metrics in the same workspace where you manage your ledger operations. To get started: Open your [Blnk Cloud](https://cloud.blnkfinance.com) workspace. If you have not already, connect your Core deployment to Cloud. For a self-hosted Core, follow [Connect your self-hosted Core](/cloud/instances/create). For a managed instance, see [Deploy a managed instance](/cloud/instances/deploy). Open the instance in Cloud, turn on monitoring in the instance settings, then copy the `Blnk Cloud DSN`. You will add it to your Core configuration in the next step. Blnk Cloud instance settings with monitoring enabled and the Blnk Cloud DSN field visible Turn on observability and add the Blnk Cloud DSN to your configuration file. ```bash blnk.env wrap theme={"system"} BLNK_ENABLE_OBSERVABILITY=true BLNK_CLOUD_DSN=https://@observe.blnk.cloud/ ``` ```json blnk.json wrap theme={"system"} { "enable_observability": true, "cloud_dsn": "https://@observe.blnk.cloud/" } ``` Restart the server and worker processes after updating configuration. Check server logs for a message that monitoring export is enabled, then open your Blnk Cloud monitoring workspace and confirm Blnk data is arriving. It may take a few minutes for the first data to show up. Blnk Cloud monitoring workspace showing a distributed trace timeline When you export to Blnk Cloud, sensitive values (such as tokens and connection strings) are removed before they leave your instance. *** ## Connect to other tools If your team already runs a dedicated observability stack, you can export Blnk logs, traces, and metrics to Jaeger, Prometheus, Grafana, OpenTelemetry Collector, Datadog, or any backend you operate. Blnk exposes traces and metrics via OpenTelemetry and Prometheus. Logs are written to process output; forward them with your log shipper. Set `BLNK_ENABLE_OBSERVABILITY=true` on the server and workers. ```bash blnk.env wrap theme={"system"} BLNK_ENABLE_OBSERVABILITY=true ``` ```json blnk.json wrap theme={"system"} { "enable_observability": true } ``` Restart both processes after updating configuration. Collect `stdout` and `stderr` with your log agent or shipper and forward to any backend. Set the traces OTLP HTTP endpoint in the environment for the server and worker. Jaeger, Grafana Tempo, and the OpenTelemetry Collector accept OTLP: ```bash blnk.env wrap theme={"system"} OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://jaeger:4318 ``` After restart, call any API endpoint and confirm traces for service `BLNK` appear in your backend (Jaeger UI defaults to port `16686`). Blnk supports two export modes. Use one or both. Scrape `GET /metrics` from the server and worker. Targets depend on your setup: | Setup | Server target | Worker target | | :--------------------------------------------------- | :---------------------- | :---------------------- | | [Docker Compose](/home/deploy#setting-up-monitoring) | `server:5001` | `worker:5004` | | Local Prometheus | `localhost:5001` | `localhost:5004` | | Managed service | Your server metrics URL | Your worker metrics URL | ```yaml No auth wrap theme={"system"} scrape_configs: - job_name: blnk-server metrics_path: /metrics static_configs: - targets: ["server:5001"] - job_name: blnk-worker metrics_path: /metrics static_configs: - targets: ["worker:5004"] ``` ```yaml Auth enabled wrap theme={"system"} scrape_configs: - job_name: blnk-server metrics_path: /metrics static_configs: - targets: ["server:5001"] authorization: type: Bearer credentials: "" - job_name: blnk-worker metrics_path: /metrics static_configs: - targets: ["worker:5004"] authorization: type: Bearer credentials: "" ``` `/metrics` never accepts `X-Blnk-Key`. Set the [metrics bearer token](/advanced/configuration/server-security#metrics-endpoint) in Core before using the Auth enabled scrape config. ```bash blnk.env wrap theme={"system"} BLNK_METRICS_BEARER_TOKEN= ``` ```json blnk.json wrap theme={"system"} { "server": { "metrics_bearer_token": "" } } ``` Restart both processes after changing the token. Verify scrapes against each target: ```bash No auth wrap theme={"system"} curl -X GET "http://localhost:5001/metrics" curl -X GET "http://localhost:5004/metrics" ``` ```bash Auth enabled wrap theme={"system"} curl -X GET "http://localhost:5001/metrics" \ -H "Authorization: Bearer " curl -X GET "http://localhost:5004/metrics" \ -H "Authorization: Bearer " ``` Set `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT` to send metrics over `OTLP HTTP` to an OpenTelemetry Collector or compatible backend. Blnk pushes on a 60-second interval. ```bash blnk.env wrap theme={"system"} OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://otel-collector:4318 ``` After restart and a test API call, verify monitoring export is working end to end. Server and worker logs should report that monitoring export is active. ```bash wrap theme={"system"} curl http://localhost:5001/health curl http://localhost:5004/health ``` ```json Server 200 OK wrap theme={"system"} { "status": "UP" } ``` ```json Worker 200 OK wrap theme={"system"} { "status": "UP", "service": "worker" } ``` Probe metrics on both processes (omit the header when no bearer token is configured): ```bash wrap theme={"system"} curl -H "Authorization: Bearer " http://localhost:5001/metrics | grep blnk_ curl -H "Authorization: Bearer " http://localhost:5004/metrics | grep blnk_ ``` Check traces in your OTLP backend and log lines in your log store. *** ## Common Blnk scenarios Match the symptom to the signal that answers it fastest. Start with the recommended signal, then drill into the others if you still need context. **Start with:** Traces Open a trace for a slow request and compare step duration across the timeline. Look for time spent in database writes, queue enqueue, or external calls rather than assuming the API handler is the bottleneck. If traces are unavailable, check server logs for the request window and correlate with queue processing logs on the worker. **Start with:** Logs and [queue monitoring](/advanced/monitoring-port) Confirm workers are running and processing jobs. In the queue dashboard, check depth and whether items are moving. In logs, look for worker startup messages, processing errors, or repeated retries on the same transaction. A healthy queue should show enqueue activity on the server and dequeue/processing activity on workers. **Start with:** Metrics Watch background processing signals over time. Rising processing duration or falling throughput can mean hash-chain sealing or related integrity work is falling behind. Compare server and worker metrics together. Delay on the worker while the API looks healthy often points to queue backlog rather than API saturation. **Start with:** Logs, then traces Filter logs around the spike window and capture the error message and `transaction_id` or request ID when present. That gives you the failure reason without opening a trace first. When you need timing context, open the trace for the same request and see which step returned the error or timed out. **Start with:** Metrics, then logs Check rejection counters grouped by reason (for example insufficient funds, overdraft limit, lock contention). A sudden change in one reason usually narrows the investigation quickly. Use logs to find example rejected transactions and confirm whether the spike matches a product change, balance issue, or concurrency pattern. **Start with:** Metrics and logs Sustained worker retries often mean transactions are failing transiently or hitting lock contention. Compare retry rate with queue processing duration on the worker. In logs, search for retry messages on the worker and note the error text. Repeated retries on the same balance pair may indicate hot-lane or lock contention worth correlating with transaction volume. **Start with:** Logs, then traces Find the inflight transaction ID in logs and follow commit or void processing on the worker. Confirm the original inflight transaction exists and the follow-up request reached Core. If processing started but did not finish, use a trace on the commit or void request to see whether failure happened in validation, balance update, or queue handling. **Start with:** Traces and metrics Compare request latency and error rate before and after the deploy window. Open traces for representative slow and failed requests in each window and note which step changed. Check that both server and worker were restarted with the same monitoring export and configuration changes. Mismatched config between processes is a common post-deploy regression. **Start with:** Logs After restart, server logs should report that monitoring export is enabled. For Blnk Cloud, confirm monitoring is on in the instance settings and the Blnk Cloud DSN is set. For self-hosted export, confirm OTLP or scrape targets are configured on **both** server and worker. Cloud ingestion can take a few minutes for the first data. For self-hosted setups, send a test API request and check your trace backend, metrics scraper, and log store separately. Monitoring export is disabled or the process was not restarted after enabling it. Set `enable_observability=true` on the server and worker, then restart both processes. [Secure mode](/advanced/configuration/server-security#secure-mode) is on but `metrics_bearer_token` is not set. Configure a bearer token and restart Core. A bearer token is required but missing or invalid. Include `Authorization: Bearer ` in scrape or curl requests. Blnk exports traces over OTLP HTTP. Confirm `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` is set on both server and worker. Use port **4318** for OTLP HTTP, not gRPC port 4317. The queue dashboard is at `/monitoring` on the worker port. Metrics are at `/metrics` on the server and worker ports. Monitoring export must be enabled for `/metrics` to be registered. **Start with:** Metrics Establish normal ranges for resource use, transaction throughput, queue processing time, and rejection rate during steady traffic. Use those baselines for alerts rather than fixed thresholds copied from another environment. Confirm scrapes or Cloud ingestion succeed on a schedule so gaps in data do not look like an incident. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Queue Monitoring Source: https://docs.blnkfinance.com/advanced/monitoring-port Configure and use the queue monitoring port for Blnk server metrics. Available in version 0.10.3 and later. The **queue monitoring port** allows you to expose internal queue metrics and monitoring endpoints for your Blnk server. This is useful for operational dashboards, health checks, and integration with monitoring tools (such as Prometheus, Grafana, or custom dashboards). ### Example configuration ```json blnk.json theme={"system"} { "queue": { "monitoring_port": "5004" } } ``` * **Environment variable:** `BLNK_QUEUE_MONITORING_PORT` * **Dashboard URL:** Visit `http://localhost:5004/monitoring` in your browser to access the real-time monitoring dashboard. *** ## Web Dashboard The dashboard provides a visual overview of your queue system: Queue Monitoring Dashboard | Feature | Description | | :-------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | Queue Size | Visualizes the number of tasks in each queue, broken down by status (active, pending, aggregating, scheduled, retry, archived, completed). | | Tasks Processed | Shows a graph of succeeded and failed tasks over time. | | Queue Table | Lists all queues with their current state, size, memory usage, latency, processed/failed counts, and error rates. | | Actions | Provides controls for queue management and inspection. | This dashboard helps operators and developers monitor queue activity, track performance, and troubleshoot issues visually. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Queue Recovery Source: https://docs.blnkfinance.com/advanced/queue-recovery Recover stuck queued transactions that failed to process. Available in version 0.13.2 and later. Transactions normally move from `QUEUED` to `APPLIED`, `INFLIGHT`, or `REJECTED`. In rare occurrences, transactions may get stuck in `QUEUED` for example after a worker crash, Redis issues, or enqueue failures. Queue recovery finds these transactions and re-enqueues them for processing. *** ## How it works Blnk now automatically checks for and recovers any transactions stuck in queue due to any number of reasons. You can also trigger recovery manually with the [Queue Recovery](/reference/queue-recovery) endpoint. ```bash wrap theme={"system"} curl -X POST "http://localhost:5001/transactions/recover?threshold=5m" \ -H "X-blnk-key: " ``` `threshold` defines how long a transaction must be QUEUED before it is considered stuck. With `threshold=5m`, Blnk recovers only transactions that have been waiting at least 5 minutes; anything newer is left alone. Minimum value allowed: 2 minutes. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Secure Your Blnk Server Source: https://docs.blnkfinance.com/advanced/secure-blnk Enable secure mode, manage secret keys, and follow best practices for a secure environment. This guide walks you through running your Blnk server in secure mode and protecting your master key. For day-to-day API access, create [scoped API keys](/api-keys/overview). Before you start, ensure you have a working instance of Blnk Core: Local install or Blnk Cloud. *** ## Enable authentication Enable secure mode by setting `server.secure` to `true` and providing a strong `server.secret_key`: ```bash blnk.env theme={"system"} BLNK_SERVER_SECURE=true BLNK_SERVER_SECRET_KEY=your_strong_secret_key ``` ```json blnk.json theme={"system"} { "server": { "secure": true, "secret_key": "your_strong_secret_key" } } ``` When secure mode is enabled, every API request must include a valid key in the `X-Blnk-Key` header. Requests without a key are rejected. The `secret_key` value becomes your **master key**. It has full access to every endpoint on the server, so use it only for administrative tasks and initial setup. For regular service traffic, create scoped API keys. Pass the master key in the `X-Blnk-Key` header: ```bash cURL wrap theme={"system"} curl -X GET "http://localhost:5001/ledgers" \ -H "X-Blnk-Key: " ``` Do not commit the master key to version control. In production, store it in a secret manager or inject it through environment variables. *** ## About the master key The master key is the `secret_key` from your configuration. Unlike [scoped API keys](/api-keys/overview), it is not bound to an owner or scopes, and it can call any endpoint on the server. Blnk also uses it to sign outbound [webhook](/webhooks/global-webhooks) and [transaction hook](/webhooks/transaction-hooks) deliveries. We recommend using the master key only for initial setup and administrative tasks listed below. For regular API calls, use the scoped keys you create. 1. **Create scoped keys.** Create your first scoped keys with `POST /api-keys`. After that, services should use scoped keys, not the master key. 2. **Manage keys across owners.** List, create, and revoke keys for any owner by passing `owner` in the request. Scoped keys can manage keys only within their own `owner_id`. See [Owner context](/api-keys/owner-context). 3. **Manage hooks.** Register, update, list, and delete [transaction hooks](/webhooks/transaction-hooks). Hook management rejects scoped keys, even with `hooks:*` scopes. 4. **Verify webhooks.** Blnk signs outbound deliveries with `X-Blnk-Signature` using `server.secret_key`. Use the same secret on your receiver to verify authenticity. See [Webhook security](/webhooks/overview#webhook-security-signature-verification). *** ## Security check list Enabling secure mode is only the start. Work through this checklist to protect your master key, keep configuration out of version control, and limit API access to what each service needs. * Use a strong, randomly generated master key * Never share or commit it to version control * Store it in environment variables or a secret management tool * Rotate it on a regular schedule * Exclude `blnk.json` from version control (`.gitignore`) * Store sensitive configuration in environment variables * Implement secure secret rotation procedures * Follow the principle of least privilege: use scoped keys for services, not the master key * Regularly review API key permissions. See [Manage API keys](/api-keys/manage-keys) * Register and manage [transaction hooks](/webhooks/transaction-hooks) with the master key only * Track failed authentication attempts * Monitor API key usage patterns * Set up alerts for suspicious activity * Regularly review access logs * Keep all components up to date * Monitor security advisories * Schedule regular maintenance windows *** ## Error handling Structured errors are available from Blnk Core 0.15.0 and later. When authentication fails, Blnk returns `401 Unauthorized`. These errors apply whether you use the master key or a scoped key. | Code | When it happens | | :--------------------- | :------------------------------------------------ | | `AUTH_MISSING_API_KEY` | No `X-Blnk-Key` header was sent with the request. | | `AUTH_INVALID_API_KEY` | The key value is unknown or malformed. | | `AUTH_EXPIRED_API_KEY` | The key has expired or has been revoked. | ```json 401 Unauthorized wrap theme={"system"} { "error": "Authentication required. Use X-Blnk-Key header", "error_detail": { "code": "AUTH_MISSING_API_KEY", "message": "Authentication required. Use X-Blnk-Key header" } } ``` To resolve the error: | Code | What to do | | :--------------------- | :-------------------------------------------------------------------------------------------------- | | `AUTH_MISSING_API_KEY` | Add the `X-Blnk-Key` header to your request. | | `AUTH_INVALID_API_KEY` | Verify the key value wasn't truncated or swapped with another environment's key. | | `AUTH_EXPIRED_API_KEY` | Create a replacement key before revoking the old one. See [Manage API keys](/api-keys/manage-keys). | Permission errors for missing scopes are covered in [Scopes](/api-keys/scopes#error-handling). Owner and delegation errors are covered in [Owner context](/api-keys/owner-context#error-handling). *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Manage API Keys Source: https://docs.blnkfinance.com/api-keys/manage-keys List, revoke, and delegate API keys to keep your Blnk deployment secure over time. After you create a scoped key, you'll audit what's active, revoke keys you no longer need, and rotate keys before they expire. If you haven't created a key yet, start with [Scoped API keys](/api-keys/overview). Listing, revoking, and delegating keys requires the master key or a scoped key with the matching `api-keys:*` scopes. See [Scopes](/api-keys/scopes) for permissions and [Owner context](/api-keys/owner-context) for which keys a caller can manage. *** ## List keys List keys for an owner to see what's active. The plaintext `key` value is never returned. You get metadata such as name, scopes, expiry, and last-used timestamp. ```bash cURL wrap theme={"system"} curl -X GET "http://localhost:5001/api-keys?owner=payments-team" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.ApiKeys.list({ owner: 'payments-team', }); ``` ```go Go wrap theme={"system"} keys, resp, err := client.ApiKeys.List(&blnkgo.ListApiKeysOptions{ Owner: "payments-team", }) ``` ```python Python wrap theme={"system"} response = blnk.api_keys.list({ "owner": "payments-team", }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.apiKeys().list( ListApiKeysOptions.create() .owner("payments-team")); ``` ```json 200 OK wrap theme={"system"} [ { "api_key_id": "api_key_879f0ecb-e29f-4137-801b-1048366381db", "name": "Payments Service", "owner_id": "payments-team", "scopes": ["transactions:write", "balances:read"], "expires_at": "2027-06-13T00:00:00Z", "created_at": "2026-06-13T10:30:00Z", "last_used_at": "2026-06-13T14:22:00Z" } ] ``` *** ## Revoke a key Revoke a key when it's no longer needed or you suspect it was exposed. ```bash cURL wrap theme={"system"} curl -X DELETE "http://localhost:5001/api-keys/api_key_879f0ecb-e29f-4137-801b-1048366381db?owner=payments-team" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.ApiKeys.delete( 'api_key_879f0ecb-e29f-4137-801b-1048366381db', { owner: 'payments-team', }, ); ``` ```go Go wrap theme={"system"} resp, err := client.ApiKeys.Delete( "api_key_879f0ecb-e29f-4137-801b-1048366381db", &blnkgo.DeleteApiKeysOptions{ Owner: "payments-team", }, ) ``` ```python Python wrap theme={"system"} response = blnk.api_keys.delete( "api_key_879f0ecb-e29f-4137-801b-1048366381db", { "owner": "payments-team", }, ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.apiKeys().delete( "api_key_879f0ecb-e29f-4137-801b-1048366381db", DeleteApiKeyOptions.create() .owner("payments-team")); ``` A successful revoke returns `204 No Content` with an empty body. The key stops working on the next request. Revoking a key takes effect immediately. Deploy a replacement key before revoking the old one. *** ## Delegate key creation Available on Blnk Core 0.14.3 and later. A scoped key with `api-keys:write` can create narrower keys for its own owner, as long as it only grants scopes it already holds. See [Owner context](/api-keys/owner-context) for inheritance and cross-owner rules. ```bash cURL wrap theme={"system"} curl -X POST "http://localhost:5001/api-keys" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "name": "Nightly Reconciliation Job", "owner": "payments-team", "scopes": ["reconciliation:read"], "expires_at": "2027-01-01T00:00:00Z" }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.ApiKeys.create({ name: 'Nightly Reconciliation Job', owner: 'payments-team', scopes: ['reconciliation:read'], expires_at: '2027-01-01T00:00:00Z', }); ``` ```go Go wrap theme={"system"} expiresAt, _ := time.Parse(time.RFC3339, "2027-01-01T00:00:00Z") apiKey, resp, err := client.ApiKeys.Create(blnkgo.CreateApiKeyRequest{ Name: "Nightly Reconciliation Job", Owner: "payments-team", Scopes: []string{"reconciliation:read"}, ExpiresAt: expiresAt, }) ``` ```python Python wrap theme={"system"} response = blnk.api_keys.create({ "name": "Nightly Reconciliation Job", "owner": "payments-team", "scopes": ["reconciliation:read"], "expires_at": "2027-01-01T00:00:00Z", }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.apiKeys().create( CreateApiKeyData.create() .name("Nightly Reconciliation Job") .owner("payments-team") .scopes(List.of("reconciliation:read")) .expiresAt("2027-01-01T00:00:00Z")); ``` *** ## Rotate a key Create a new key with the same scopes or tighter ones: ```bash cURL wrap theme={"system"} curl -X POST "http://localhost:5001/api-keys" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "name": "Payments Service", "owner": "payments-team", "scopes": ["transactions:write", "balances:read"], "expires_at": "2027-06-13T00:00:00Z" }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.ApiKeys.create({ name: 'Payments Service', owner: 'payments-team', scopes: ['transactions:write', 'balances:read'], expires_at: '2027-06-13T00:00:00Z', }); ``` ```go Go wrap theme={"system"} expiresAt, _ := time.Parse(time.RFC3339, "2027-06-13T00:00:00Z") apiKey, resp, err := client.ApiKeys.Create(blnkgo.CreateApiKeyRequest{ Name: "Payments Service", Owner: "payments-team", Scopes: []string{"transactions:write", "balances:read"}, ExpiresAt: expiresAt, }) ``` ```python Python wrap theme={"system"} response = blnk.api_keys.create({ "name": "Payments Service", "owner": "payments-team", "scopes": ["transactions:write", "balances:read"], "expires_at": "2027-06-13T00:00:00Z", }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.apiKeys().create( CreateApiKeyData.create() .name("Payments Service") .owner("payments-team") .scopes(List.of("transactions:write", "balances:read")) .expiresAt("2027-06-13T00:00:00Z")); ``` Copy the plaintext `key` value from the response immediately. You won't see it again. Deploy the new key to your secret manager or environment variables. Verify the service works with the new key. Delete the old key: ```bash cURL wrap theme={"system"} curl -X DELETE "http://localhost:5001/api-keys/api_key_879f0ecb-e29f-4137-801b-1048366381db?owner=payments-team" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.ApiKeys.delete( 'api_key_879f0ecb-e29f-4137-801b-1048366381db', { owner: 'payments-team', }, ); ``` ```go Go wrap theme={"system"} resp, err := client.ApiKeys.Delete( "api_key_879f0ecb-e29f-4137-801b-1048366381db", &blnkgo.DeleteApiKeysOptions{ Owner: "payments-team", }, ) ``` ```python Python wrap theme={"system"} response = blnk.api_keys.delete( "api_key_879f0ecb-e29f-4137-801b-1048366381db", { "owner": "payments-team", }, ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.apiKeys().delete( "api_key_879f0ecb-e29f-4137-801b-1048366381db", DeleteApiKeyOptions.create() .owner("payments-team")); ``` Confirm your applications no longer reference the old key. List keys for the owner and confirm only the expected keys remain active: ```bash cURL wrap theme={"system"} curl -X GET "http://localhost:5001/api-keys?owner=payments-team" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.ApiKeys.list({ owner: 'payments-team', }); ``` ```go Go wrap theme={"system"} keys, resp, err := client.ApiKeys.List(&blnkgo.ListApiKeysOptions{ Owner: "payments-team", }) ``` ```python Python wrap theme={"system"} response = blnk.api_keys.list({ "owner": "payments-team", }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.apiKeys().list( ListApiKeysOptions.create() .owner("payments-team")); ``` *** ## Security best practices * Review your key list regularly. Look for keys with broad scopes, keys that haven't been used recently, and keys approaching their expiry date. * Create a separate key for each service or environment. * Set expiration dates and grant the minimum scopes. See [Scopes](/api-keys/scopes) before each create. * Store keys in a secret manager. Never commit them to version control. *** ## Error handling Structured errors are available from Blnk Core 0.15.0 and later. When a list, create, or revoke request fails validation or owner checks, Blnk returns `400 Bad Request` or `404 Not Found`. | Code | When it happens | | :---------------------- | :--------------------------------------------------------------------------- | | `APIKEY_INVALID` | The create request failed validation. | | `APIKEY_OWNER_REQUIRED` | The master key was used to create or list keys without an `owner` parameter. | | `APIKEY_NOT_FOUND` | The key ID is not found in the caller's owner context. | ```json 400 Bad Request wrap theme={"system"} { "error": "owner is required", "error_detail": { "code": "APIKEY_OWNER_REQUIRED", "message": "owner is required" } } ``` To resolve the error: | Code | What to do | | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | | `APIKEY_OWNER_REQUIRED` | Add the `owner` query parameter or body field when using the master key. | | `APIKEY_INVALID` | Fix the validation issue in the create request (missing fields, invalid scopes, or malformed dates). | | `APIKEY_NOT_FOUND` | Verify the key ID and that it belongs to the owner you're acting on. See [Owner context](/api-keys/owner-context#error-handling). | Delegation and cross-owner errors are covered in [Owner context](/api-keys/owner-context#error-handling). Permission errors for missing scopes are covered in [Scopes](/api-keys/scopes#error-handling). *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Scoped API Keys Source: https://docs.blnkfinance.com/api-keys/overview Create scoped API keys so each service gets only the permissions it needs. Available on Blnk Core 0.10.1 and later. Your master key has full access to your Blnk server. Use it only for admin actions, like creating and revoking API keys. For everything else, use scoped API keys. Each key can be limited to a specific owner, set of scopes, and expiry date. For example, your payments service might only need `transactions:write` and `balances:read`, while your reporting service might only need `*:read`. This keeps each service limited to the access it needs. If a key is exposed, you can revoke that key without rotating your master key or affecting the rest of your system. *** ## Before you start Scoped API keys require secure mode. When secure mode is enabled, Blnk requires every request to include a valid key in the `X-Blnk-Key` header. Start by enabling secure mode and setting a strong `secret_key` in your configuration: ```bash blnk.env theme={"system"} BLNK_SERVER_SECURE=true BLNK_SERVER_SECRET_KEY=your_strong_secret_key ``` ```json blnk.json wrap theme={"system"} { "server": { "secure": true, "secret_key": "your_strong_secret_key" } } ``` The `secret_key` becomes your master key. Use it once to create your first scoped API key with `POST /api-keys`. After that, use scoped keys for normal API traffic. Do not commit the master key to version control. In production, store it in a secret manager or inject it through environment variables. *** ## Create and use a scoped key Use the master key to create your first scoped key. In this example, we create a key for a payments service: ```bash cURL wrap theme={"system"} curl -X POST "http://localhost:5001/api-keys" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "name": "Payments Service", "owner": "payments-team", "scopes": ["transactions:write", "balances:read"], "expires_at": "2027-06-13T00:00:00Z" }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.ApiKeys.create({ name: 'Payments Service', owner: 'payments-team', scopes: ['transactions:write', 'balances:read'], expires_at: '2027-06-13T00:00:00Z', }); ``` ```go Go wrap theme={"system"} expiresAt, _ := time.Parse(time.RFC3339, "2027-06-13T00:00:00Z") apiKey, resp, err := client.ApiKeys.Create(blnkgo.CreateApiKeyRequest{ Name: "Payments Service", Owner: "payments-team", Scopes: []string{"transactions:write", "balances:read"}, ExpiresAt: expiresAt, }) ``` ```python Python wrap theme={"system"} response = blnk.api_keys.create({ "name": "Payments Service", "owner": "payments-team", "scopes": ["transactions:write", "balances:read"], "expires_at": "2027-06-13T00:00:00Z", }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.apiKeys().create( CreateApiKeyData.create() .name("Payments Service") .owner("payments-team") .scopes(List.of("transactions:write", "balances:read")) .expiresAt("2027-06-13T00:00:00Z")); ``` | Parameter | Description | | :----------- | :---------------------------------------------------------------------------------------------------------------------------- | | `name` | A readable label for the key, such as a service or environment name. | | `owner` | The team, service, or tenant that owns the key. Blnk stores this as `owner_id`. See [Owner context](/api-keys/owner-context). | | `scopes` | The permissions assigned to the key, using `resource:action` format. See [Scopes](/api-keys/scopes). | | `expires_at` | When the key stops working, in ISO 8601 format. | A successful request returns `201 Created`. On success, the response includes the plaintext key in the `key` field: ```json 201 Created wrap theme={"system"} { "api_key_id": "api_key_879f0ecb-e29f-4137-801b-1048366381db", "key": "YVLIhuIplUzLRCcT9r7DQ_jsGKCXAn39JQ3n_o-Ll2Q=", "name": "Payments Service", "owner_id": "payments-team", "scopes": ["transactions:write", "balances:read"], "expires_at": "2027-06-13T00:00:00Z", "created_at": "2026-06-13T10:30:00Z", "last_used_at": "0001-01-01T00:00:00Z", "is_revoked": false } ``` Copy the `key` value immediately and store it in your secrets manager or as an environment variable. Wire that value into the service that will call Blnk. Blnk shows the plaintext key only once. After creation, the key is hashed at rest and cannot be retrieved again. If you lose it, create a new key and revoke the old one. Pass the scoped key in the `X-Blnk-Key` header on every request. ```bash cURL wrap theme={"system"} curl -X POST "http://localhost:5001/transactions" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "precise_amount": 10000, "reference": "ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", "currency": "USD", "precision": 100, "source": "bln_a1b2c3d4-6e2d-4f89-a17b-3d5e8f2a1c94", "destination": "bln_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", "description": "Payment transfer", "allow_overdraft": true }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.Transactions.create({ precise_amount: 10000, reference: 'ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94', currency: 'USD', precision: 100, source: 'bln_a1b2c3d4-6e2d-4f89-a17b-3d5e8f2a1c94', destination: 'bln_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94', description: 'Payment transfer', allow_overdraft: true, }); ``` ```go Go wrap theme={"system"} baseURL, _ := url.Parse("http://localhost:5001/") scopedKey := os.Getenv("BLNK_SCOPED_API_KEY") client := blnkgo.NewClient(baseURL, &scopedKey) transaction, resp, err := client.Transaction.Create(blnkgo.CreateTransactionRequest{ ParentTransaction: blnkgo.ParentTransaction{ PreciseAmount: big.NewInt(10000), Reference: "ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", Currency: "USD", Precision: 100, Source: "bln_a1b2c3d4-6e2d-4f89-a17b-3d5e8f2a1c94", Destination: "bln_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", Description: "Payment transfer", }, AllowOverdraft: true, }) ``` ```python Python wrap theme={"system"} response = blnk.transactions.create({ "precise_amount": 10000, "reference": "ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", "currency": "USD", "precision": 100, "source": "bln_a1b2c3d4-6e2d-4f89-a17b-3d5e8f2a1c94", "destination": "bln_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", "description": "Payment transfer", "allow_overdraft": True, }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.transactions().create( CreateTransactions.create() .preciseAmount(10000) .reference("ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94") .currency("USD") .precision(100) .source("bln_a1b2c3d4-6e2d-4f89-a17b-3d5e8f2a1c94") .destination("bln_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94") .description("Payment transfer") .allowOverdraft(true)); ``` Blnk checks that the key is valid, not expired or revoked, and that its scopes cover the endpoint you're calling. This key has `transactions:write`, so it can create transactions but cannot create ledgers without `ledgers:write`. *** ## API key tracking When you create a record with a scoped API key, Blnk adds the key’s `api_key_id` to the resource `meta_data` under `BLNK_GENERATED_BY`. This lets you trace which key created a ledger, balance, transaction, or identity. The master key does not add this field. Only scoped API keys authenticated through `X-Blnk-Key` do. ```json API key tracking wrap theme={"system"} "meta_data": { "purpose": "customer_wallet", "BLNK_GENERATED_BY": "api_key_879f0ecb-e29f-4137-801b-1048366381db" } ``` Blnk sets `BLNK_GENERATED_BY` automatically on `POST` requests. You do not need to include it in your request body. If you send your own `meta_data`, Blnk merges this field into it. You can use `BLNK_GENERATED_BY` for audit trails, service attribution, and tenant-level tracing. For example, if each tenant or service uses its own scoped key, you can filter records by `meta_data.BLNK_GENERATED_BY` to see what that key created. *** ## Error handling Structured errors are available from Blnk Core 0.15.0 and later. When a scoped key fails authentication, Blnk returns a `401 Unauthorized` response. | Code | When it happens | | :--------------------- | :-------------------------------------------------------------------------- | | `AUTH_INVALID_API_KEY` | The key is incorrect, malformed, incomplete, or sent with the wrong header. | | `AUTH_EXPIRED_API_KEY` | The key has expired or has been revoked. | ```json 401 Unauthorized wrap theme={"system"} { "error": "API key is expired or revoked", "error_detail": { "code": "AUTH_EXPIRED_API_KEY", "message": "API key is expired or revoked" } } ``` To resolve the error: | Code | What to do | | :--------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------- | | `AUTH_INVALID_API_KEY` | Check that the full key value is being sent in the `X-Blnk-Key` header. | | `AUTH_EXPIRED_API_KEY` | Create a replacement key with the same scopes, update the service using it, then revoke the old key. See [Manage API keys](/api-keys/manage-keys). | Other authentication errors, such as a missing `X-Blnk-Key` header, are covered in [Secure your Blnk server](/advanced/secure-blnk#error-handling). *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # API Key Owner Context Source: https://docs.blnkfinance.com/api-keys/owner-context Understand how owner labels isolate API key management across teams and services. Available on Blnk Core 0.14.3 and later. Every API key belongs to an owner. The owner is a label you choose when creating the key, such as a team name, service name, or tenant ID. Blnk stores this value as `owner_id` on the key record. Owner context controls which keys a scoped key can manage. A scoped key can only create, list, and revoke keys that belong to its own `owner_id`. It cannot manage keys for another owner. *** ## Example scenario Say you create two keys under different owners: * Key `k1` has `owner_id = merchant_a` * Key `k2` has `owner_id = merchant_b` If `k1` is used to manage API keys, Blnk resolves the owner as `merchant_a`. That means `k1` can manage keys for `merchant_a`, but it cannot manage keys for `merchant_b`, even if the request includes `"owner": "merchant_b"`. The [master key](/advanced/secure-blnk#master-key) is different. It isn't bound to an owner, so it can manage keys for any owner. Pass `owner` in the request to target a specific one. *** ## Delegated key management Scoped keys can manage API keys, but only within their own owner context. This lets a team admin create narrower service keys for a team, service, or tenant without handing out the master key. For example, a key with `api-keys:read`, `api-keys:write`, and `api-keys:delete` can create, list, and revoke keys for its own `owner_id`. | Caller | What it can manage | | :----------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Master key** | Can create, list, and revoke keys for any owner. Use the `owner` field to choose which owner the request applies to. | | **Scoped API key** | Can create, list, and revoke keys only for its own `owner_id`. If the request includes a different `owner`, Blnk ignores it and uses the key's stored `owner_id`. | Two rules apply to scoped keys: 1. **Scope inheritance.** A key can only grant scopes it already has. For example, a key with only `transactions:read` cannot create another key with `transactions:write`. 2. **No cross-owner access.** The effective owner always comes from the caller’s stored `owner_id`. A key for `merchant_a` cannot create, list, or revoke keys for `merchant_b`. *** ## Error handling Structured errors are available from Blnk Core 0.15.0 and later. When a key management request breaks owner or delegation rules, Blnk returns a `400`, `403`, or `404` response. | Code | Status | When it happens | | :------------------------ | :----- | :---------------------------------------------------------------------- | | `AUTH_CROSS_OWNER_ACCESS` | `403` | A scoped key tries to manage keys for another owner. | | `AUTH_SCOPE_ESCALATION` | `403` | A scoped key tries to grant scopes it does not have. | | `APIKEY_NOT_FOUND` | `404` | The key ID does not exist in the caller’s owner context. | | `APIKEY_OWNER_REQUIRED` | `400` | The master key is used to create or list keys without an `owner` value. | Blnk does not always return `403` for cross-owner requests. For example, trying to revoke another owner’s key returns `404 APIKEY_NOT_FOUND` instead of `403`. This prevents the response from revealing whether that key exists under another owner. Listing keys with `?owner=another-team` returns `403 AUTH_CROSS_OWNER_ACCESS`, because the request is explicitly trying to access another owner’s key list. Example response: ```json 403 Forbidden wrap theme={"system"} { "error": "cannot grant scopes broader than caller", "error_detail": { "code": "AUTH_SCOPE_ESCALATION", "message": "cannot grant scopes broader than caller" } } ``` To resolve the error: | Code | What to do | | :------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------- | | `AUTH_CROSS_OWNER_ACCESS` | Verify the caller's `owner_id` matches the owner you're targeting. Use the [master key](/advanced/secure-blnk#master-key) for cross-owner admin. | | `AUTH_SCOPE_ESCALATION` | Remove scopes from the create request that the caller doesn't hold, or create the key with the master key instead. | | `APIKEY_NOT_FOUND` | Confirm the key ID exists in the caller's owner context. A `404` may mean the key belongs to another owner. | | `APIKEY_OWNER_REQUIRED` | Include `owner` in the create body or list query when using the master key. | *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # API Key Scopes Source: https://docs.blnkfinance.com/api-keys/scopes Choose the right permissions for each API key using 'resource:action' scopes. Scopes control what an API key is allowed to do. Each scope uses `resource:action` format. For example, `ledgers:read` allows a key to read ledgers, while `transactions:write` allows it to create transactions. Instead of giving every service full access, use scopes to create keys that match what each service actually does. These patterns cover most setups: | Use case | Scopes | What the key can do | | :------------------ | :--------------------------------------------------- | :------------------------------------- | | Read-only reporting | `ledgers:read`, `balances:read` | View ledgers and balances | | Payment processing | `transactions:write`, `balances:read` | Create transactions and check balances | | Identity management | `identities:write`, `identities:read` | Create and view identities | | Key administration | `api-keys:read`, `api-keys:write`, `api-keys:delete` | Manage keys within its owner context | Avoid `*:*` unless you truly need full access. For most cases, create a scoped key with only the permissions required. *** ## How scopes work Each scope has two parts: a resource and an action, separated by a colon: `resource:action`. The resource defines the area of Blnk the key can access. The action defines what the key can do in that area. | Resource | Description | | :----------------- | :--------------------- | | `*` | All resources | | `ledgers` | Ledger management | | `balances` | Balance operations | | `accounts` | Account operations | | `identities` | Identity management | | `transactions` | Transaction processing | | `balance-monitors` | Balance monitoring | | `api-keys` | API key management | | `search` | Search operations | | `reconciliation` | Reconciliation tasks | | `metadata` | Metadata management | | `backup` | Backup operations | Blnk maps HTTP methods to actions automatically: | Action | HTTP methods | Description | | :------- | :--------------------- | :--------------------------- | | `*` | Any | All actions on the resource | | `read` | `GET`, `HEAD` | View operations | | `write` | `POST`, `PUT`, `PATCH` | Create and modify operations | | `delete` | `DELETE` | Delete operations | You can use `*` as a wildcard on either side. `ledgers:*` grants all ledger actions. `*:read` grants read access to every resource. Hook management requires the [master key](/advanced/secure-blnk#master-key). Scoped keys cannot manage hooks. *** ## Error handling Structured errors are available from Blnk Core 0.15.0 and later. When a scoped key is valid but lacks permission for the endpoint you called, Blnk returns a `403 Forbidden` response. | Code | When it happens | | :------------------------------ | :------------------------------------------------------------- | | `AUTH_INSUFFICIENT_PERMISSIONS` | The key is missing the scope required for that endpoint. | | `AUTH_UNKNOWN_RESOURCE` | The request path does not map to a known Blnk resource. | | `AUTH_MASTER_KEY_REQUIRED` | The endpoint requires the master key, such as hook management. | ```json 403 Forbidden wrap theme={"system"} { "error": "Insufficient permissions for transactions:write", "error_detail": { "code": "AUTH_INSUFFICIENT_PERMISSIONS", "message": "Insufficient permissions for transactions:write" } } ``` To resolve the error: | Code | What to do | | :------------------------------ | :---------------------------------------------------------------------------------------------------------------------- | | `AUTH_INSUFFICIENT_PERMISSIONS` | Check which scope the endpoint needs, then create a new key with that scope or call an endpoint the key already covers. | | `AUTH_UNKNOWN_RESOURCE` | Verify the request path matches a supported API route. | | `AUTH_MASTER_KEY_REQUIRED` | Use the [master key](/advanced/secure-blnk#master-key) for that operation. | *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Balance from Source Source: https://docs.blnkfinance.com/balances/balance-from-source Compute balance directly from transactions instead of using the default running balance. Available in version 0.10.1 and later. The [Balance from Source](/reference/balance-from-source) endpoint allows you to compute a balance directly from its transactions instead of using the default running balance. This is useful when you need to verify balance accuracy, perform audits, or ensure consistency by calculating balances independently from the stored balance values. ```bash cURL wrap theme={"system"} curl -X GET "http://YOUR_BLNK_INSTANCE_URL/balances/{balance_id}?from_source=true" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.LedgerBalances.get( 'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f', { from_source: true, }, ); ``` ```go Go wrap theme={"system"} at, _ := time.Parse(time.RFC3339, "2024-04-22T15:28:03+00:00") balance, resp, err := client.LedgerBalance.GetHistorical( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", at, true, ) ``` ```python Python wrap theme={"system"} response = blnk.ledger_balances.get( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", { "from_source": True, }, ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.ledgerBalances().get( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", GetBalanceRequest.create().fromSource(true)); ``` ```json Response theme={"system"} { "balance": { "balance": 9620000, "balance_id": "bin_be16c4a1-b5a6-4b64-a733-de2f6b24813d", "credit_balance": 9620000, "currency": "USD", "debit_balance": 0, "ledger_id": "" } } ``` *** ## How it works When you call the balance from source endpoint, Blnk reconstructs the balance by: 1. **Getting all applied transactions:** Blnk retrieves all applied transactions for the balance and aggregates them together. As a result, it may take longer to process for balances with extensive transaction history. 2. **Calculating credits and debits:** It aggregates all credit transactions (money moving into the balance) and debit transactions (money moving out of the balance) to compute the total credit balance, debit balance, and net balance. 3. **Returning the computed balance:** The final computed balance, reflecting the exact state based on transaction history, is returned in the response. Balances at specific past timestamps. Point-in-time copies of running balances. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Balance Monitoring Source: https://docs.blnkfinance.com/balances/balance-monitoring Monitor balances and get notified via webhook when they meet set conditions. Balance monitors let you keep track of balances in your Blnk Ledger. This is useful for scenarios where balances should meet specific thresholds. You can monitor all 3 sub-balances of a ledger balance - credit balance (`credit_balance`), debit balance (`debit_balance`) and total balance (`balance`). *** ## Why monitor balances? 1. **Fraud detection:** Unusual balance changes can be an early indication of fraudulent activities. Monitoring can trigger alerts for suspicious transactions and ensure timely intervention. 2. **Regulatory compliance:** Many financial regulations require institutions to maintain specific balance thresholds. Real-time balance monitoring makes it easy to comply with these regulations. 3. **Customer notifications:** Customers can be notified in real-time if their balance crosses a specific threshold. It can also be used for segmenting your customers in your application. 4. **Operational efficiency:** Instantly knowing when a balance reaches a certain threshold can trigger automatic actions, such as transferring funds between accounts or purchasing assets. *** ## Set up balance monitors Choose the balance to watch, the sub-balance field, the comparison operator, and the threshold value. Use the same `precision` you use when recording transactions for that currency. In this example, you want to get notified when `debit_balance` is greater than 1,000.00 (`value: 100000`, `precision: 100`). Call `POST /balance-monitors` with your condition: ```bash cURL wrap theme={"system"} curl -X POST "http://localhost:5001/balance-monitors" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "balance_id": "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", "condition": { "field": "debit_balance", "operator": ">", "value": 100000, "precision": 100 }, "description": "Tier 1 Account" }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.BalanceMonitor.create({ balance_id: 'bln_0be360ca-86fe-457d-be43-daa3f966d8f0', condition: { field: 'debit_balance', operator: '>', value: 100000, precision: 100, }, description: 'Tier 1 Account', }); ``` ```go Go wrap theme={"system"} monitor, resp, err := client.BalanceMonitor.Create(blnkgo.MonitorData{ BalanceID: "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", Condition: blnkgo.MonitorCondition{ Field: "debit_balance", Operator: blnkgo.OperatorGreaterThan, Value: 100000, Precision: 100, }, Description: "Tier 1 Account", }) ``` ```python Python wrap theme={"system"} response = blnk.balance_monitor.create({ "balance_id": "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", "condition": { "field": "debit_balance", "operator": ">", "value": 100000, "precision": 100, }, "description": "Tier 1 Account", }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.balanceMonitor().create( MonitorData.create() .balanceId("bln_0be360ca-86fe-457d-be43-daa3f966d8f0") .condition(MonitorCondition.create() .field("debit_balance") .operator(">") .value(100000) .precision(100)) .description("Tier 1 Account")); ``` | Field | Description | | :------------ | :-------------------------------------------------------------------------------------- | | `balance_id` | Unique identifier of the balance to be monitored. | | `condition` | Object representing the condition to be satisfied. | | `field` | Sub-balance to monitor: `debit_balance`, `credit_balance`, or `balance`. | | `operator` | Comparison operator. See [Supported operators](#supported-operators). | | `value` | Threshold the `field` is compared against. | | `precision` | Precision applied to `value`. Must match how you record transactions for that currency. | | `description` | Label for the monitor. Empty when omitted. | You can include `meta_data` in the request to attach custom data to the monitor. Blnk stores the monitor and returns a unique `monitor_id`. When the condition is met, you receive a `balance.monitor` webhook event. ```json Response wrap theme={"system"} { "monitor_id": "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", "balance_id": "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", "condition": { "field": "debit_balance", "operator": ">", "value": 100000, "precision": 100 }, "description": "Tier 1 Account", "created_at": "2024-02-20T05:56:58.257315054Z" } ``` | Field | Description | Type | | :----------- | :------------------------------------------ | :------- | | `monitor_id` | Unique identifier for your balance monitor. | `string` | | `created_at` | Date and time of creation. | `string` | Save the returned `monitor_id` - you need it to view, update, or delete the monitor later. *** ## Supported operators This is a list of all supported operators by the Balance monitor: | Operators | Symbol | Description | | :----------------------- | :----- | :----------------------------------------------------------------------------- | | Greater than | > | Checks if the specified balance in `field` is greater than `value` | | Less than | \< | Checks if the specified balance in `field` is less than `value` | | Equal to | = | Checks if the specified balance in `field` is exactly equal to `value` | | Not equal to | != | Checks if the specified balance in `field` is not equal to `value` | | Greater than or equal to | >= | Checks if the specified balance in `field` is greater than or equal to `value` | | Less than or equal to | \<= | Checks if the specified balance in `field` is less than or equal to `value` | *** ## View a balance monitor Call `GET /balance-monitors/{monitor_id}` to retrieve one monitor by ID. ```bash cURL wrap theme={"system"} curl -X GET "http://YOUR_BLNK_INSTANCE_URL/balance-monitors/{monitor_id}" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.BalanceMonitor.get('{monitor_id}'); ``` ```go Go wrap theme={"system"} monitor, resp, err := client.BalanceMonitor.Get("mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0") ``` ```python Python wrap theme={"system"} response = blnk.balance_monitor.get("mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0") ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.balanceMonitor().get("mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0"); ``` ```json Response wrap theme={"system"} { "monitor_id": "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", "balance_id": "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", "condition": { "field": "debit_balance", "operator": ">", "value": 100000 }, "description": "Tier 1 Account", "created_at": "2024-02-20T05:56:58.257315054Z" } ``` *** ## List all balance monitors Call `GET /balance-monitors` to retrieve every monitor in your ledger. ```bash cURL wrap theme={"system"} curl -X GET "http://YOUR_BLNK_INSTANCE_URL/balance-monitors" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.BalanceMonitor.list(); ``` ```go Go wrap theme={"system"} monitors, resp, err := client.BalanceMonitor.List() ``` ```python Python wrap theme={"system"} response = blnk.balance_monitor.list() ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.balanceMonitor().list(); ``` ```json Response wrap theme={"system"} [ { "monitor_id": "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", "balance_id": "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", "condition": { "field": "debit_balance", "operator": ">", "value": 100000 }, "description": "Tier 1 Account", "created_at": "2024-02-20T05:56:58.257315054Z" } ] ``` *** ## List monitors for a balance Call `GET /balance-monitors/balances/{balance_id}` to retrieve every monitor attached to a specific balance. The path parameter is the **balance ID**, not a monitor ID. ```bash cURL wrap theme={"system"} curl -X GET "http://YOUR_BLNK_INSTANCE_URL/balance-monitors/balances/{balance_id}" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.BalanceMonitor.listByBalanceId( '{balance_id}', ); ``` ```go Go wrap theme={"system"} monitors, resp, err := client.BalanceMonitor.ListByBalanceID( "{balance_id}", ) ``` ```python Python wrap theme={"system"} response = blnk.balance_monitor.list_by_balance_id( "{balance_id}", ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.balanceMonitor().listByBalanceId( "{balance_id}"); ``` ```json Response wrap theme={"system"} [ { "monitor_id": "mon_c33db397-7475-4930-a719-4be110e437f5", "balance_id": "bln_71e707a0-f811-43ef-b5ec-2c7445d57d18", "condition": { "field": "debit_balance", "operator": ">", "value": 200, "precision": 100, "precise_value": 10000 }, "description": "High debit alert", "created_at": "2026-06-13T00:33:25.473364Z" } ] ``` *** ## Update a balance monitor Call `PUT /balance-monitors/{monitor_id}` and provide the updated conditions in the request body. ```bash cURL wrap theme={"system"} curl -X PUT "http://YOUR_BLNK_INSTANCE_URL/balance-monitors/{monitor_id}" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "balance_id": "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", "condition": { "field": "debit_balance", "operator": ">", "value": 100000 }, "description": "Tier 1 Account" }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.BalanceMonitor.update( 'mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0', { balance_id: 'bln_0be360ca-86fe-457d-be43-daa3f966d8f0', condition: { field: 'debit_balance', operator: '>', value: 100000, }, description: 'Tier 1 Account', }, ); ``` ```go Go wrap theme={"system"} monitor, resp, err := client.BalanceMonitor.Update("mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", blnkgo.MonitorData{ BalanceID: "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", Condition: blnkgo.MonitorCondition{ Field: "debit_balance", Operator: blnkgo.OperatorGreaterThan, Value: 100000, }, Description: "Tier 1 Account", }) ``` ```python Python wrap theme={"system"} response = blnk.balance_monitor.update( "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", { "balance_id": "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", "condition": { "field": "debit_balance", "operator": ">", "value": 100000, }, "description": "Tier 1 Account", }, ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.balanceMonitor().update( "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", MonitorData.create() .balanceId("bln_0be360ca-86fe-457d-be43-daa3f966d8f0") .condition(MonitorCondition.create() .field("debit_balance") .operator(">") .value(100000)) .description("Tier 1 Account")); ``` ```json Response wrap theme={"system"} { "monitor_id": "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", "balance_id": "bln_0be360ca-86fe-457d-be43-daa3f966d8f0", "condition": { "field": "debit_balance", "operator": ">", "value": 100000 }, "description": "Tier 1 Account", "created_at": "2024-02-20T05:56:58.257315054Z" } ``` *** ## Delete a balance monitor Call `DELETE /balance-monitors/{monitor_id}` to remove a monitor. ```bash cURL wrap theme={"system"} curl -X DELETE "http://YOUR_BLNK_INSTANCE_URL/balance-monitors/{monitor_id}" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.BalanceMonitor.delete('mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0'); ``` ```go Go wrap theme={"system"} deleted, resp, err := client.BalanceMonitor.Delete( "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", ) ``` ```python Python wrap theme={"system"} response = blnk.balance_monitor.delete( "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0", ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.balanceMonitor().delete( "mon_e0e77b0c-4985-472a-9bf5-76a48b0259b0"); ``` ```json 200 OK wrap theme={"system"} { "message": "BalanceMonitor deleted successfully" } ``` A subsequent `GET /balance-monitors/{monitor_id}` returns `404` with `BAL_MONITOR_NOT_FOUND`. See [Delete balance monitor](/reference/delete-balance-monitor) for the full reference. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Balance Snapshots Source: https://docs.blnkfinance.com/balances/balance-snapshots Capture and manage periodical balance snapshots for accurate financial reporting. Available in version 0.8.4 and later. Blnk Finance offers a balance snapshotting feature that enables users to access historical balance data at any specific point in time. This ensures precise financial reporting and analysis while maintaining efficient storage and retrieval systems. *** ## Quick overview * **Purpose:** Records a daily snapshot of each balance to preserve historical data, enabling users to review past financial states for auditing, reporting, or analytical purposes. * **Frequency:** * Blnk takes one snapshot per day per balance when triggered. * If the snapshot is called multiple times in a single day, it records only the first instance, capturing the state of all balances at that time. * Subsequent calls on the same day will not create additional snapshots for balances already captured but will record snapshots for any new balances that lack a snapshot for that day. * The next snapshot for all balances can only be captured on the following day. * **Recommended timing:** It’s best to take snapshots at midnight or at the user’s defined end-of-day period for consistency, as this minimizes disruptions and aligns with typical business closing times, providing a clear picture of daily financial positions. Users have flexibility in determining when to trigger snapshots, but each balance is limited to one snapshot per day. *** ## Triggering a snapshot Call the [Balance Snapshots](/reference/balances-snapshots) endpoint. This action initiates a snapshot of all balances, capturing their current state at the time of the request for future reference. ```bash cURL wrap theme={"system"} curl -X POST "http://YOUR_BLNK_INSTANCE_URL/balances-snapshots" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.LedgerBalances.createSnapshot({}); ``` ```go Go wrap theme={"system"} result, resp, err := client.LedgerBalance.CreateSnapshot(blnkgo.CreateBalanceSnapshotRequest{}) ``` ```python Python wrap theme={"system"} response = blnk.ledger_balances.create_snapshot({}) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.ledgerBalances().createSnapshot(); ``` ```json Response theme={"system"} { "message": "Snapshottings in progress. Should be completed shortly." } ``` Historical balances from stored snapshots. *** ## Key considerations * **Daily snapshots and reconstruction:** Since snapshots are taken once per day per balance, balances between snapshots are reconstructed using transaction data. * **Optimal snapshot timing:** Choosing the right time to trigger snapshots is crucial for maintaining consistency and accuracy. Users should initiate snapshots at their preferred end-of-day period, such as midnight or another consistent business closing time. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Retrieving Historical Balances Source: https://docs.blnkfinance.com/balances/historical-balances Retrieve accurate historical balance information. Available in version 0.8.4 and later. The [Historical Balances](/reference/historical-balances) endpoint allows users to retrieve balances (identified by `balance_id`) at a particular historical point in time, specified by the timestamp parameter. It leverages Blnk's balance snapshot feature to provide accurate historical data for financial reporting, auditing, or analysis. ```bash cURL wrap theme={"system"} curl -X GET "http://YOUR_BLNK_INSTANCE_URL/balances/{balance_id}/at?timestamp=2024-04-22T15:28:03.123456Z" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.LedgerBalances.getAt( 'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f', { timestamp: '2024-04-22T15:28:03.123456Z', }, ); ``` ```go Go wrap theme={"system"} at, _ := time.Parse(time.RFC3339, "2024-04-22T15:28:03.123456Z") balance, resp, err := client.LedgerBalance.GetHistorical( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", at, false, ) ``` ```python Python wrap theme={"system"} response = blnk.ledger_balances.get_at( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", { "timestamp": "2024-04-22T15:28:03.123456Z", }, ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.ledgerBalances().getAt( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", GetBalanceAtRequest.create() .timestamp("2024-04-22T15:28:03.123456Z")); ``` Always format the date input as 'YYYY-MM-DDTHH:MM:SS+00:00' (e.g., 2024-04-22T15:28:03.123456Z). ```json Example response theme={"system"} { "balance": { "balance": 9620000, "balance_id": "bin_be16c4a1-b5a6-4b64-a733-de2f6b24813d", "credit_balance": 9620000, "currency": "USD", "debit_balance": 0, "ledger_id": "" }, "timestamp": "2025-02-24T08:55:26.976106Z" } ``` *** ## How it works When querying for a balance at a specific timestamp, Blnk follows these steps to ensure accuracy and reliability: 1. **Identify the most recent snapshot:** The system retrieves the most recent snapshot taken before the requested timestamp. Snapshots are daily records of balances, captured manually by users as described in the Balance Snapshots feature. If no snapshot is found, Blnk builds the historical balance from genesis (using transactions only). 2. **Apply intervening transactions:** It then applies all transactions that occurred between the time of the snapshot and the requested timestamp. This reconstructs the balance state by accounting for any credits, debits, or other financial activities that took place during that period. 3. **Return the computed balance:** The final computed balance, reflecting the exact state at the requested timestamp, is returned in the response. Point-in-time copies of running balances. ### Reconstruct balances from source Available on version 0.10.1 or later. You can choose to bypass balance snapshots and directly reconstruct your balances from their transactions alone. To do this, include the query parameter `from_source=true` in your request URL: ```bash cURL wrap theme={"system"} curl -X GET "http://YOUR_BLNK_INSTANCE_URL/balances/{balance_id}/at?timestamp=2024-04-22T15:28:03.123456Z&from_source=true" \ -H "X-blnk-key: " ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.LedgerBalances.getAt( 'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f', { timestamp: '2024-04-22T15:28:03.123456Z', from_source: true, }, ); ``` ```go Go wrap theme={"system"} at, _ := time.Parse(time.RFC3339, "2024-04-22T15:28:03.123456Z") balance, resp, err := client.LedgerBalance.GetHistorical( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", at, true, ) ``` ```python Python wrap theme={"system"} response = blnk.ledger_balances.get_at( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", { "timestamp": "2024-04-22T15:28:03.123456Z", "from_source": True, }, ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.ledgerBalances().getAt( "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", GetBalanceAtRequest.create() .timestamp("2024-04-22T15:28:03.123456Z") .fromSource(true)); ``` ```json Response theme={"system"} { "balance": { "balance": 9620000, "balance_id": "bin_be16c4a1-b5a6-4b64-a733-de2f6b24813d", "credit_balance": 9620000, "currency": "USD", "debit_balance": 0, "ledger_id": "" }, "timestamp": "2025-02-24T08:55:26.976106Z" } ``` *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Internal Balances Source: https://docs.blnkfinance.com/balances/internal-balances Learn how to create and use internal balances for your organization. In your application, there are scenarios when your balances interact with your organization accounts or external balances. As a principle, it is crucial for you to have an organized way of tracking these interactions in your ledger. This is why Blnk creates the [General Ledger](/ledgers/general-ledger). The main difference between the general ledger and other ledgers is that it is meant to group balances owned by your organization. We refer to these balances as "Internal balances." The General Ledger is created by default when you deploy your Blnk server. **Let's dive in ✨** What we'll cover … 1. [What are internal balances?](#1-what-are-internal-balances) 2. [Creating an internal balance](#2-creating-an-internal-balance) 3. [Naming conventions](#3-naming-conventions) 4. [Representing external accounts](#4-representing-external-accounts) *** ## 1: What are internal balances? An internal balance is a ledger balance owned by your organization, e.g., revenue, fees, loans, etc. You can use an internal balance to track interactions between your own accounts as an organization and your customers accounts managed by your application. For instance, you own an online e-commerce app that assigns a wallet to every signed up user. Two things happen: 1. Every time a user pays for something on your app with their wallet, their balance is debited and you earn revenue. 2. Every time a user funds their wallet, a fee is debited from the user's wallet and paid somewhere (the e-commerce startup or the payment processor). There are two destinations that have not been defined - revenue and fees. These have to be represented as balances held and managed by the organization. To do this, you need to create these balances in the General Ledger and provide them as the destination in your transaction record request to complete the transaction workflow. *** ## 2: Creating an internal balance Blnk makes this simple by automating the creation and identification of balances in the General Ledger. When a transaction is recorded and a balance is referenced using the "@" prefix, Blnk checks to see if it exists in the General Ledger. If the specified balance doesn't exist, it is automatically created, and the transaction is processed accordingly. Internal balances are created this way to ensure that only General Ledger balances that participate in your [money movement map](/ledgers/money-movement-map) are stored and tracked in your ledger. Continuing our earlier example, we can represent revenue with `@Revenue` and fees with `@Fees`. A transaction request from the user's wallet when they pay for something on the app would look like this: ```bash cURL wrap theme={"system"} curl -X POST "http://localhost:5001/transactions" \ -H "X-Blnk-Key: " \ -H "Content-Type: application/json" \ -d '{ "precise_amount": 75000000, "reference": "ref_1298122819291982", "currency": "USD", "precision": 100, "source": "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", "destination": "@Revenue", "description": "Fund with starting balance amount" }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.Transactions.create({ precise_amount: 75000000, reference: 'ref_1298122819291982', currency: 'USD', precision: 100, source: 'bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f', destination: '@Revenue', description: 'Fund with starting balance amount', }); ``` ```go Go wrap theme={"system"} transaction, resp, err := client.Transaction.Create(blnkgo.CreateTransactionRequest{ ParentTransaction: blnkgo.ParentTransaction{ PreciseAmount: big.NewInt(75000000), Reference: "ref_1298122819291982", Currency: "USD", Precision: 100, Source: "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", Destination: "@Revenue", Description: "Fund with starting balance amount", }, }) ``` ```python Python wrap theme={"system"} response = blnk.transactions.create({ "precise_amount": 75000000, "reference": "ref_1298122819291982", "currency": "USD", "precision": 100, "source": "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", "destination": "@Revenue", "description": "Fund with starting balance amount", }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.transactions().create( CreateTransactions.create() .preciseAmount(75000000) .reference("ref_1298122819291982") .currency("USD") .precision(100) .source("bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f") .destination("@Revenue") .description("Fund with starting balance amount")); ``` ```bash Blnk CLI wrap theme={"system"} blnk transactions create ``` ```json Response wrap theme={"system"} { "amount": 750000, "precision": 100, "precise_amount": 75000000, "transaction_id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d", "reference": "ref_1298122819291982", "source": "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", "destination": "@Revenue", "currency": "USD", "description": "Fund with starting balance amount", "status": "QUEUED", "created_at": "2024-12-21T01:36:46.997063436Z" } ``` When you post a transaction to a new internal balance, the balance is created with the same asset class specified in the transaction, e.g., in the example above, `@Revenue` is a USD internal balance. *** ## 3: Naming conventions When choosing names for your internal balances, it's important to use consistent and descriptive naming schemes. This ensures easy recall and reduces the risk of errors when posting transactions. Here are some things to note when naming your internal balances: 1. Always use the @ prefix to automatically add it to the General Ledger. 2. Use names that clearly describe the balance's purpose, e.g., `@Revenue`, `@Fees`, `@World`, etc. 3. Avoid using the same name for internal balances that serve the same function but represent different asset classes. For example, instead of naming two balances `@Revenue-one` in EUR and the other in USD, name them `@RevenueUSD` and `@RevenueEUR` for better clarity and easier reference in your integration. 4. Use camel case (`@OnlineRevenue`) or underscores (`@online_revenue`) to separate words for better readability. Avoid including spaces to prevent formatting issues. 5. Keep it short to facilitate easier referencing. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Overview Source: https://docs.blnkfinance.com/balances/introduction Learn how balances work in Blnk Ledger balances (or balances, for short) are used to represent store of value in your Blnk Ledger, i.e., accounts, wallets, card balance, etc. They also represent the source or destination of a transaction record. All ledger balances in the Blnk Ledger each have 6 main balance attributes: | Attribute | Description | | :------------------------ | :------------------------------------------------------------------------ | | `balance` | The current value held in the ledger balance | | `credit_balance` | The total sum of all amounts received by a ledger balance | | `debit_balance` | The total sum of all amounts sent by a ledger balance | | `inflight_balance` | The net amount held inflight for a balance | | `inflight_credit_balance` | The total sum of all amounts waiting to be received by a ledger balance | | `inflight_debit_balance` | The total sum of all amounts waiting to be deducted from a ledger balance | **Important to note:** Balance amounts are immutable and can only be updated through transactions. You cannot manually set or alter a balance amount directly. *** ## Creating a balance To create a balance in Blnk, call the **Create Balance** endpoint: ```bash cURL wrap theme={"system"} curl -X POST "http://localhost:5001/balances" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "ledger_id": "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc", "currency": "USD", "meta_data": { "first_name": "Alice", "last_name": "Hart", "account_number": "1234567890" } }' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.LedgerBalances.create({ ledger_id: 'ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc', currency: 'USD', meta_data: { first_name: 'Alice', last_name: 'Hart', account_number: '1234567890', }, }); ``` ```go Go wrap theme={"system"} balance, _, err := client.LedgerBalance.Create(blnkgo.CreateLedgerBalanceRequest{ LedgerID: "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc", Currency: "USD", MetaData: blnkgo.MetaData{ "first_name": "Alice", "last_name": "Hart", "account_number": "1234567890", }, }) if err != nil { log.Fatal(err) } fmt.Println("Balance Created:", balance.BalanceID) ``` ```python Python wrap theme={"system"} response = blnk.ledger_balances.create({ "ledger_id": "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc", "currency": "USD", "meta_data": { "first_name": "Alice", "last_name": "Hart", "account_number": "1234567890", }, }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.ledgerBalances().create( CreateLedgerBalance.create() .ledgerId("ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc") .currency("USD") .metaData(Map.of( "first_name", "Alice", "last_name", "Hart", "account_number", "1234567890" ))); ``` ```bash Blnk CLI wrap theme={"system"} blnk balances create ? Ledger ID: ? Currency: ? Metadata (JSON format): ``` ```json Response expandable theme={"system"} { "balance": 0, "version": 0, "inflight_balance": 0, "credit_balance": 0, "inflight_credit_balance": 0, "debit_balance": 0, "inflight_debit_balance": 0, "ledger_id": "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc", "identity_id": "", "balance_id": "bln_e39a239a-a6ca-4509-b0d9-29dcc5630f8a", "indicator": "", "currency": "USD", "created_at": "2024-07-05T08:13:18.882616461Z", "meta_data": { "customer_internal_id": "1234", "customer_name": "Jerry" } } ``` All newly created balances in the Blnk Ledger start from 0. You cannot create a ledger balance with a predefined balance amount. *** ## Queued balances Available in version 0.8.3 and later. You can track total transaction amounts in queue per balance via the `queued_credit_balance` and `queued_debit_balance` attributes. These attributes show the cumulative value of all queued transactions that will affect a balance once they're processed. When you query a balance, you'll see: 1. **queued\_credit\_balance**: Total amount of incoming transactions waiting to be processed. 2. **queued\_debit\_balance**: Total amount of outgoing transactions waiting to be processed. This helps you estimate the potential impact of queued transactions on your balance before they're processed. Use queued balances to check if a balance has pending transactions. If either `queued_credit_balance` or `queued_debit_balance` is greater than 0, there are transactions waiting in the queue for that balance. Queued balances are estimates and may change as transactions are processed or if queue processing fails. Always verify final balances after queue processing completes. To view the queued balances of a ledger balance: ```bash cURL wrap theme={"system"} curl -X GET 'http://YOUR_BLNK_INSTANCE_URL/balances/{balance_id}?with_queued=true' \ -H 'X-blnk-key: ' ``` ```typescript TypeScript wrap theme={"system"} const response = await blnk.LedgerBalances.get( 'bln_e39a239a-a6ca-4509-b0d9-29dcc5630f8a', { with_queued: true, }, ); ``` ```go Go wrap theme={"system"} balance, resp, err := client.LedgerBalance.Get( "bln_e39a239a-a6ca-4509-b0d9-29dcc5630f8a", &blnkgo.GetBalanceRequest{WithQueued: true}, ) ``` ```python Python wrap theme={"system"} response = blnk.ledger_balances.get( "bln_e39a239a-a6ca-4509-b0d9-29dcc5630f8a", {"with_queued": True}, ) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.ledgerBalances().get( "bln_e39a239a-a6ca-4509-b0d9-29dcc5630f8a", GetBalanceRequest.create().withQueued(true)); ``` ```json Response {9-10} theme={"system"} { "balance": 0, "version": 0, "inflight_balance": 0, "credit_balance": 0, "inflight_credit_balance": 0, "debit_balance": 0, "inflight_debit_balance": 0, "queued_credit_balance": 0, "queued_debit_balance": 0, "ledger_id": "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc", "identity_id": "", "balance_id": "bln_e39a239a-a6ca-4509-b0d9-29dcc5630f8a", "indicator": "", "currency": "USD", "created_at": "2024-07-05T08:13:18.882616461Z", "meta_data": { "customer_internal_id": "1234", "customer_name": "Jerry" } } ``` ### Calculating expected and available balances Use these formulas to understand your balance position: 1. **Expected balance:** What your balance will be after all queued credits are processed ``` expected_balance = current_balance + queued_credit_balance ``` 2. **Available balance:** How much you can safely spend or transfer (accounting for pending debits) ``` available_balance = current_balance - inflight_debit_balance - queued_debit_balance ``` **Example:** If your current balance is $100, you have $50 in queued credits, and \$20 in queued debits: * Expected balance: $100 + $50 = \$150 * Available balance: $100 - $0 - $20 = $80 *** ## Multi-currency balances Blnk enables you to create and manage balances in multiple currencies within your ledger. When building multi-currency wallets with Blnk, keep the following in mind: 1. **Standardize [precision](/transactions/precision) across currencies:** Most fiat currencies have a precision of 100. However, when managing currencies with different precision values, such as crypto and fiat, it's crucial to use the highest precision value for all transactions in your ledger. This ensures consistency when calculating transactions and converting exchange rates between currencies. *** ## Balance overdrafts An overdraft occurs when a transaction reduces a ledger balance below zero, resulting in a negative balance. This means that the amount debited exceeds the available funds in the account. While traditionally seen as a deficit, in Blnk, a negative balance is simply another balance state and can be used flexibly, especially depending on the type of financial product. In Blnk, implementing overdrafts is straightforward. By setting the `allow_overdraft` attribute when initiating a transaction, you can allow a ledger balance to fall below 0. This feature is valuable as it provides insights and reflects the position of a balance, especially useful in certain product contexts, such as lending or line-of-credit services. See the following: [Applying overdrafts](/transactions/overdrafts) ### Why negative balances? In Blnk, a negative balance is simply a balance state and doesn’t necessarily mean there’s a problem. For example, it can simply show borrowed funds, as expected in products like loans or credit, where it reflects the amount owed. It could also mean that a balance has more debits than credits - this is especially relevant for internal balances like `@World`. To learn more, see the following: [Internal balances](/balances/internal-balances) Moreover, a negative balance is often temporary and can revert to a positive balance under certain conditions, such as: 1. **Incoming Credits:** When new funds are credited to the account, they reduce or eliminate the negative balance. 2. **Scheduled Repayments:** For products like loans, regular repayments or scheduled deposits will move the balance back toward positive territory over time, ultimately making it positive if the full overdraft is repaid. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Blnk CLI Source: https://docs.blnkfinance.com/changelog/blnk-cli Latest releases, fixes, and improvements to the Blnk CLI. ## Fixes & improvements Fixed field filters being ignored on list commands. Flags such as **`--status`**, **`--currency`**, and **`--balance-id`** now apply correctly across `ledgers`, `balances`, `transactions`, `identities`, and `reconciliations`. ```bash bash wrap theme={"system"} blnk transactions l --status APPLIED # returns only APPLIED blnk transactions l --status "APPLIED,QUEUED" # matches either status ``` ## Connect Improved the flow for connecting your CLI to Blnk Core so you can start running commands against your instance. ## List & search List commands now default to direct DB search. On older Blnk Core versions without direct DB search, the CLI falls back to Typesense. You can also filter results by field across list commands. ## Fixes & improvements 1. Added **`reindex`**, **`community`**, and **`docs`** to create and list search reindex jobs, open the Blnk community, or jump to the docs from your terminal. 2. Expanded **`update`** flows with guided prompts, including identity ID handling for balance updates. 3. Broader command coverage for ledgers, balances, transactions, identities, and reconciliations. 4. Improved table output, help text, flags, and status coloring across commands. 5. Stronger error handling for invalid arguments, API failures, and prompt flows. 6. Removed legacy **cloud** CLI surface and unused dependencies. 7. **Node.js 18+** is now required. 8. Added field filtering to list and query flows. (A follow-up fix in **1.8.2** resolved filters not applying correctly.) *** # Blnk Cloud Source: https://docs.blnkfinance.com/changelog/blnk-cloud Latest features, releases, and improvements to Blnk Cloud. ## Monitoring Monitor activity across your Cloud workspace from a dedicated monitoring view. Track operational signals and review what needs attention without leaving the dashboard. Cloud monitoring dashboard ## Mobile view Blnk Cloud is now optimized for mobile browsers. Review balances, transactions, and key workspace details on smaller screens. Blnk Cloud mobile layout ## Balance activity When you open a balance from a table, the side modal shows balance activity for the last 7 days so you can scan recent movement without opening the full details page. Balance side modal showing the last 7 days of activity Learn more in our [Balance details guide](/cloud/balances/details). ## Command palette Open the command palette from anywhere in Cloud to jump to pages, run actions, and navigate faster with the keyboard. Command palette open in Blnk Cloud See [Shortcuts](/cloud/start/shortcuts) for keyboard navigation tips. ## Workspace domains Verify domains your workspace owns from **Settings > Workspace**. Teammates with a matching email address can request to join, and you can review those requests under **Join requests**. Workspace settings Domains section showing verified domains and Join requests tabs Learn more in our [Workspace guide](/cloud/organization/workspace). ## Custom Apps library Browse, install, and manage Custom Apps from the Apps library in your workspace. Install ledger-aware workflows built by your team or third-party developers. Apps library listing available Custom Apps Learn more in our [Custom Apps guide](/cloud/apps/overview) and [Install a Custom App](/cloud/apps/install-app). ## Custom Apps Build and run private, ledger-aware workflows inside Blnk Cloud. Custom Apps let your team connect external systems, automate checks, and launch purpose-built tools around the ledger. Custom Apps in Blnk Cloud Learn more in our [Custom Apps guide](/cloud/apps/overview). ## API key expiration Update an API key's expiration date from the key details view without recreating the key. API key details view with expiration date controls Learn more in our [API Keys guide](/cloud/auth/api-keys). ## Balance lineage and allocation When creating a balance, enable lineage tracking and choose an allocation strategy (`FIFO`, `LIFO`, or `PROPORTIONAL`) to tag incoming funds and control how they are spent. Create balance form with lineage tracking and allocation strategy Learn more in our [Balances guide](/cloud/balances/overview). ## Metadata templates Create reusable sets of metadata keys and import them when creating or editing ledgers, balances, transactions, or identities. Metadata templates settings page Learn more in our [Metadata templates guide](/cloud/organization/metadata-templates). ## Multiple workspaces You can now switch between workspaces or create a new one. Click the workspace menu on the sidebar to select an existing workspace or **Add workspace** to set up a new organization profile. Setup a new Blink workspace form with organization name, size, team, and country fields When creating a new workspace, enter your organization name, size, team, and country, then click **Create workspace** to get started. ## Transaction filters **Has child transaction** filter added to transaction filters so you can quickly find transactions that have child transactions. ## Metadata filters **Key does not exist** option added to metadata filters so you can filter for records where a metadata key is missing. ## UI improvements Hover over truncated text in tables and views to see the full content in a tooltip. ## Timezones You can now choose to see dates and times in **UTC** or your **local time** across your data. When you switch timezone, all relevant views and date filters use your chosen timezone-including ledgers, reconciliations, and other data tables. Open the dropdown on the left sidebar (the same menu where you access Settings), choose **Set timezone**, and pick UTC or Local time; it applies everywhere. Sidebar with Settings expanded showing Set timezone option with UTC and Local time choices ## New Features * **MCP (Model Context Protocol)**: Interact with your Blnk Core and Cloud workspace from AI assistants like Cursor, Claude Desktop, and ChatGPT. Get your MCP endpoint from **Settings > MCP** and add it to your tool. Learn more in our [MCP guide](/cloud/integrations/mcp). * **API Keys**: You can now create and manage API keys for your workspace. Use API keys to authenticate programmatic access to Blnk Cloud and integrate with your applications and workflows. Learn more in our [API Keys guide](/cloud/auth/api-keys). * **Alert statuses**: Create and manage custom alert statuses with names and colours for your organization. Add statuses from **Settings > Alert statuses**, then use them when updating alerts. Add alert status form with name and colour fields Learn more in our [Alert statuses guide](/cloud/alerts/alert-statuses). ## Audit Logs With Audit Logs, you can now monitor all activities happening in your Cloud workspace from one place. It tells you what was done, who did it, and when it happened. You can also filter your logs by category or use custom filters to find specific activities. Audit logs page showing activity history ## Identity tokenization We've added PII (Personally Identifiable Information) tokenization to Blnk Cloud, allowing you to decode tokenized fields from your ledger. You can also tokenize identity information from your dashboard if you need to. To see how it works, read our guide: [PII tokenization](https://guide.cloud.blnkfinance.com/identities/pii#pii-tokenization). Tokenize modal showing field selection options for PII tokenization ## Update Identity You can now edit any identity information directly from the identity details page. Update names, contact details, addresses, and other identity fields as needed. Edit existing identity ## Effective date now available You can now record transactions with their original financial date, instead of their created date. This is great for backdating transactions when you need to. ## Update metadata You can now add new metadata or update existing metadata of any record after creation. Update Metadata Workflow interface ## Historical Balances Retrieval Retrieve accurate historical balance information. Particularly useful for financial reporting, auditing, and analysis. View historical balance ## Assign an identity to a existing balance You can create and link multiple balances to a single identity within your Ledger. Any transaction record associated with a linked balance is also automatically mapped to the corresponding identity. Assign identities ## Saved Views Create and save custom filter combinations across all data tables. Save frequently used filter sets like "This Month's Failed Transactions" and switch between them instantly. Available on all tables. Saved Views interface showing filter cards and dropdown ## IP Whitelisting IP whitelisting allows you to control which IP addresses can access your deployed Core instances on Blnk Cloud. This provides an additional layer of security by restricting access to only trusted sources. For more information, see our [IP Whitelisting Guide](/cloud/instances/whitelist). IP whitelisting ## UI/UX Enhancements * Added a “What’s New” section to the sidebar for quick access to recent updates and features. ## New Features * **Deploy a managed Core instance**: Deploy a managed Core instance directly from Blnk Cloud. Blnk takes care of the deployment and maintenance of your Core instance, and automatically connects it to your Blnk Cloud workspace. For more information, see our [Deploy Instance Guide](/cloud/instances/deploy#deploy-a-managed-core-instance). * **Two-Factor Authentication**: To add an extra layer of protection to your account, you can enable two-factor authentication. Two-factor authentication setup process with authenticator app instructions To get started, toggle it on, and follow the instructions to set it up using any of our supported authenticator apps (Authy, Google Authenticator, etc.). ## Improvements * Significant Cloud performance upgrades. * Advanced filters added to ledger, balances, transactions, and identity tables for more precise data exploration. ## Simplified Onboarding Onboarding is now two steps, with instance connection moved into the dashboard: 1. Create your organization 2. Select a Cloud plan For detailed guidance, see our [Getting Started Guide](/cloud/start/guide). ## UI Refresh * New sidebar and dashboard designs. * Empty states added across all tables. * New table designs for ledgers, transactions, balances, identities, and anomalies - each with a one-click refresh button. * Redesigned settings for a cleaner experience. * Private connection notice added to instance connection flow. ## Insights New **Insights page** that gives you a clear view of what's happening across your ledger. They provide quick, visual reports on balances, transactions, identities, etc. so you can monitor activity at a glance. Main Insights dashboard showing all 6 default visualizations Six default visualizations are generated when you connect your Core: * Total transactions * Total balances * Total identities * Transaction trend * Balance trend * Identities trend Create custom insights with advanced filtering operators like *between ranges*, *in lists*, *greater than*, and more. For more information, see our [Insights](/cloud/insights/overview). ## Other Improvements * Instance connection switched from direct to [**Query Agent**](/cloud/instances/create). * Vault feature removed. * General ledger is now **always pinned to the top** of the ledgers page. ## New Onboarding Flow New onboarding with the following steps: 1. Create your organization 2. Select a Cloud plan 3. Connect an instance ## UI & Usability Improvements * Updated font from *Inter Display* → *Inter Variable* for better readability. * Transactions table now shows **Effective Date** instead of Created At. * Added hover tooltips to display both **Created At** and **Effective Date**. * Introduced a new **Cloud Home page**. ## Google SSO Log in seamlessly with your Google account for faster access to your workspace. If your email is linked to a Google account, you can now use it to sign in. ## Fixes & Improvements * All amounts now display to the smallest unit, with hover support to view the raw value and the applied precision. * Added effective date display on transactions tables, with hover support to view the original creation timestamp. * Minor UI refinements and bug fixes for a smoother experience. *** # Blnk Core Source: https://docs.blnkfinance.com/changelog/blnk-core Latest features, releases, and improvements. ## Fixes & improvements 1. **Queue backpressure:** Blnk now rejects new queue enqueues when Redis memory usage or pending task count exceeds configured limits. Affected requests return `503 Service Unavailable` with `error_detail.code` set to `QUEUE_BACKPRESSURE`. Backpressure is enabled by default. [Learn more →](/advanced/configuration/queue) 2. **Typesense memory backpressure:** Reindex and search indexing now retry gracefully when Typesense hits memory limits instead of failing immediately. [Reindex Typesense →](/search/typesense/reindex) 3. **Typesense reindex:** `effective_date` is now preserved when rebuilding the Typesense index from Postgres. 4. General bug fixes and improvements. ## Fixes & improvements 1. **Transaction filter:** `POST /transactions/filter` now returns `parent_transaction` instead of an empty string. 2. **Queued inflight commits:** Committing against a multi-leg parent transaction id now works when the request goes through the queue. 3. **Search/reindex:** Transaction search and reindex now include `parent_transaction`, `precise_amount`, and `precision`. 4. **Reconciliation uploads:** Improved reconciliation file upload handling and cleaned up file-handle closing. 5. General bug fixes and improvements. ## Structured API errors Error responses include a structured `error_detail` object with a stable code and clear messages. The response also includes the `error` field. ```json Error response theme={"system"} { "error": "transaction not found", "error_detail": { "code": "TXN_NOT_FOUND", "message": "transaction not found", "details": { } } } ``` If your app checks status codes or error message text, review the [0.15.0 migration guide](/changelog/v15-migration) before upgrading. [API error codes →](/advanced/error-codes) ## Queued inflight commit and void Commit and void for inflight transactions now go through the queue by default. Bulk commit and void also follow the same default. Enable `skip_queue` in the request body when you need the previous synchronous behavior. [Commit & void inflight →](/transactions/inflight/updating-inflight) ## Transaction hash chain Blnk now supports an optional global hash chain to detect tampering in your transaction history and confirm ledger integrity on demand. It is disabled by default; enable it in config and run `blnk verify-chain` from your Core deployment. [Transaction hashing →](/transactions/hash) ## Fixes & improvements 1. Added DELETE endpoints for balance monitors and [identities](/reference/delete-identity). 2. **Reconciliation webhooks:** Blnk now sends `reconciliation.completed` and `reconciliation.failed` webhooks when a run completes or fails. [Start reconciliation](/reference/start-reconciliation) and [Instant reconciliation](/reference/instant-reconciliation) now return only `reconciliation_id` in the start response. [0.15.0 migration guide →](/changelog/v15-migration#reconciliation-response-changed) · [Supported events →](/webhooks/events#reconciliations) 3. Removed `rate`, `currency_multiplier`, and `modification_ref` from API responses. 4. Added a `system.error` webhook for internal failures during async processing. 5. Increased default worker and database pool limits. 6. Added configurable [request and upload size caps](/advanced/configuration/server-security#request-and-payload-limits). 7. [Bulk create](/reference/bulk-transactions) and [instant reconciliation](/reference/instant-reconciliation) now enforce max **10,000** items per request. 8. General bug fixes and performance improvements. ## Fixes & improvements 1. **Bulk transaction validation:** The [Create bulk transactions](/reference/bulk-transactions) endpoint now validates each item in the `transactions` array before processing. Invalid fields return `400 Bad Request` with indexed error messages. 2. **Logging levels:** Transaction processing, reconciliation progress, and search indexing messages that reflect normal workflow progress are now logged at **info** level instead of **error**, so operational logs are easier to filter and alert on. ## Remote monitoring exporter Blnk now supports optional remote export of logs, traces, and metrics through a Cloud DSN. Set **`BLNK_CLOUD_DSN`** to forward observability data to your Blnk Cloud monitoring project. Exported logs have sensitive values redacted. [Monitoring in Blnk →](/advanced/monitoring) ## Fixes & improvements 1. **Scheduled inflight commits:** Fixed a bug where [scheduled inflight commits](/transactions/inflight/updating-inflight#schedule-inflight-commits) did not commit at the time you set. Transactions with `inflight_commit_date` now commit automatically as expected. 2. **Typesense search:** Fixed a bug that prevented transactions from reliably indexing into [Typesense search](/search/typesense/introduction), so recorded transactions could not be found when searching. ## Security hardening When you manage API keys with a non-master key, Blnk now limits you to your own `owner_id`: you can create, list, and revoke keys for that owner only, and you cannot grant scopes you don't already have. The master key still has full access to create, list, and revoke keys for any owner. [Learn more →](/api-keys/owner-context) ## Fixes & improvements 1. **Hook management:** Creating, updating, viewing, listing, and deleting [transaction hooks](/webhooks/transaction-hooks) now requires the master key. Regular API keys return `403 Forbidden`. ## Bulk inflight updates Blnk Core now supports bulk commit and bulk void operations for independently-created inflight transactions. Use the [**Bulk commit endpoint**](/reference/bulk-commit-inflight) to commit multiple inflight transactions in one request, including partial commits with `amount` or `precise_amount` per transaction. Use the [**Bulk void endpoint**](/reference/bulk-void-inflight) to void multiple inflight transactions in one request. Bulk requests accept up to **100** items and return per-transaction results, so one failed transaction does not stop the rest of the batch. The response includes `succeeded`, `failed`, and a `results` array with each transaction's status, error code, and message when applicable. ## Fixes & improvements 1. **Synchronous refunds:** The [Refund transaction](/reference/refund-transaction) endpoint now accepts an optional `{"skip_queue": true}` body to process refunds synchronously. Requests without a body keep the existing queued behavior. 2. **Deterministic identity creation:** The [Create new identity](/reference/create-identity) endpoint now honors caller-supplied `identity_id` values when they use the `idt_` prefix and a valid UUID suffix. Duplicate deterministic identity ids now return a conflict response, making concurrent create flows easier to handle. 3. **Filter validation:** [Search via Direct DB](/search/db/filtering) now rejects unknown filter operators instead of silently dropping those filters and running a broader query. ## Scheduled inflight commits You can now set **`inflight_commit_date`** when recording an inflight transaction (**`inflight: true`**). This lets Blnk reserve funds immediately like a normal inflight transaction and **automatically commits** at the date and time you specify, without calling manually committing the transaction yourself. [Schedule inflight commits →](/transactions/inflight/updating-inflight#schedule-inflight-commits) ## Performance improvements This release focuses on improving how Blnk performs under high-traffic. In internal benchmarks under heavy contention and burst load, processing throughput improved by **up to about 5×**; your results depend on workload shape, how much traffic overlaps on the same balances, and how you configure the server. [Handling hot balances →](/guides/hot-balances) [Transaction configuration →](/advanced/configuration/transactions) [Queue configuration →](/advanced/configuration/queue) ## Fixes & improvements 1. **Update Metadata response:** The [Update Metadata](/reference/update-metadata) endpoint now returns **`meta_data`** in the response instead of `metadata`. [View migration guide →](/changelog/v14-migration) ## Fixes & improvements 1. **Transaction filter bug fix:** Fixed transaction filters to return precision. 2. **Edit and delete matching rules:** You can now edit and delete matching rules for your reconciliation workflows. ## Fixes & improvements * Reconciliation improvements and bug fixes. * Introduced logical operators to direct DB filters. ## Fixes & improvements * Fixed a bug where the `balance_id` filter returned an internal server error when you [search via Direct DB](/search/db/filtering). ## Search via Database Filter ledgers, balances, transactions, and identities directly from your database using the new Filter API. To use, just send POST requests to `/{collection}/filter` with JSON filters, sorting, and pagination; no Typesense setup required. [Read docs](/search/db/filtering) ## Fixes & improvements 1. **Unique reference constraint:** Blnk now enforces a unique database index on `transactions.reference` (`idx_transactions_reference_unique`), strengthening idempotency guarantees at the storage layer. This release includes a breaking database change: transaction `reference` values must be unique. Upgrade fails if your database already contains duplicate references. Review the [0.13.2 migration guide](/changelog/v13-2-migration) before upgrading. ## Reindex Typesense Populate a new or empty Typesense instance using data from your existing database. Useful when deploying a fresh Typesense instance, migrating to a new cluster, or recovering from Typesense data loss. [Read docs](/search/typesense/reindex) ## Introducing Lineage You can now track the source of funds on balances (“fund lineage”) and control how debits are allocated across sources (FIFO, LIFO, or proportional). This improves traceability for reporting and audits. [Read docs](/transactions/lineage) ## Fixes & improvements 1. **Webhook request signing**: Outbound webhook deliveries now include `X-Blnk-Signature` and `X-Blnk-Timestamp` headers (HMAC-SHA256), so you can verify webhook authenticity. 2. **Improved graceful shutdown**: Enhanced graceful shutdown to ensure pending transactions finish before the server stops. 3. **Fixed PII secret key requirement**: Fixed a bug to ensure that PII only works when you set a PII secret key in your config. 4. General bug fixes and performance improvements. ## Fixes & improvements 1. **Applied Cloud indexes** to the Core database to improve speed and performance when retrieving data on your Cloud dashboard. 2. **Fixed Search date field updates**: Resolved an issue where date fields were incorrectly updated in the [Search](/search/typesense/introduction) index when metadata was modified. 3. **Precise distribution support**: [Multiple sources](/transactions/multiple-sources) and [multiple destinations](/transactions/multiple-destinations) now support passing precise amounts in the distribution array using the `precise_distribution` parameter. This enables accurate distribution when working with `precise_amount` values. ## Fixes & improvements 1. **Fixed Typesense index errors**: Resolved issues with Typesense indexing for transaction records, ensuring reliable search functionality. 2. **Effective date search and sorting**: You can now search and sort transactions by `effective_date`. When `effective_date` is not set, it automatically defaults to the same value as `created_at`, ensuring consistent date handling across all transactions. **Reindexing required**: Old transactions that were not previously indexed in Typesense will need to be reindexed to take advantage of these improvements. You can check out this [reindexing script](https://github.com/stkegoul/blnk-reindex) from a community member. ## Enhanced API key security API keys are now hashed using SHA-256 before storage, significantly improving security posture. Plain text API keys can only be retrieved during creation-they are never stored or returned in list responses. This release includes breaking changes related to API key security. Existing API keys will stop working after upgrading. Please review the [migration guide](/changelog/v12-migration) before upgrading. ## Fixes & improvements 1. **Enhanced reconciliation support**: Reconciliation now supports amounts with more than 2 decimal places and currency codes longer than 3 characters. 2. **System error webhooks**: We now send system errors (e.g. duplicate reference) as webhooks, making it easier to monitor and troubleshoot system issues. 3. General bug fixes and performance improvements. ## Bug fixes & improvements 1. General bug fixes and improvements. ## Fixes & improvements 1. Improved webhook delivery performance for faster and more reliable concurrent webhook delivery. 2. You can now tune webhook processing performance with the new [`webhook_concurrency`](/advanced/configuration/queue#queue-settings) configuration. ## Fixes & improvements 1. Improved reconciliation performance for faster processing of large reconciliation batches. ## Typesense JOINs support You can now query related data across collections in a single search. For example, this makes it easier to fetch balances with their associated identity details, or filter identities based on their balance information. ```bash Sample request theme={"system"} curl -X POST "http://YOUR_BLNK_INSTANCE_URL/search/balances" \ -H "X-Blnk-Key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "q": "*", "include_fields": "$identities(*)" // Fetch identity details for each balance }' ``` ```json Response {15-18} theme={"system"} { "facet_counts": [], "found": 145, "hits": [ { "document": { "balance": "-13166600", "balance_id": "bln_0c2f1ab7-a40d-4e23-a5dc-daad54ddf3c1", "created_at": 1763641569, "credit_balance": "34461100", "currency": "EUR", "currency_multiplier": 0, "debit_balance": "47627700", "id": "bln_0c2f1ab7-a40d-4e23-a5dc-daad54ddf3c1", "identities": { "first_name": "Debra", "last_name": "Barrett" }, "identity_id": "idt_4dd12897-f085-41f2-955f-790ed3c0a7fb", "indicator": "", "inflight_balance": "0", "inflight_credit_balance": "0", "inflight_debit_balance": "0", "inflight_expires_at": 1763641569, "ledger_id": "ldg_6dc543d3-1138-4ea8-8072-844398c735a1", "meta_data": { "created_by": "system-api" }, "version": 19 }, "highlight": {}, "highlights": [] } ], "out_of": 148, "page": 1, "request_params": { "collection_name": "balances", "per_page": 1, "q": "*" }, "search_cutoff": false, "search_time_ms": 8 } ``` This returns balance records with their full identity details joined automatically, reducing the need for multiple API calls. ## Fixes & improvements 1. **Enhanced transaction precision validation**: The system now rejects transactions with non-integer precision values to prevent database failures and balance inconsistencies. 2. **Default general ledger indexing**: The system now automatically ensures the default general ledger exists in Typesense search index during initialization. 3. **API key tracking improvements**: Fixed an issue where API key usage tracking (`UpdateLastUsed`) was not correctly updating records by `api_key_id`, ensuring accurate API key activity monitoring. ## Fixes & improvements 1. You can now commit inflight bulk transactions created with the queue (i.e. `skip_queue: false`) using their batch IDs. 2. [Queued balances](/balances/introduction#queued-balances) now omit inflight transactions. ## Fixes & improvements 1. **Fixed metadata update bug**: Resolved issue where Typesense search index wasn't being updated when existing records' metadata was modified. 2. **[Get transaction by reference](/reference/get-transaction-by-reference)**: You can now retrieve transactions using their `reference` number. See also: [Verifying transactions](/transactions/introduction#verifying-transactions). 3. **[Update ledger name](/reference/update-ledger-name)**: You can now rename existing ledgers in your Blnk Core. 4. **[Link existing balance to identity](/reference/update-balance-identity)**: You can update the identity linked to a balance or attach an existing balance to an identity. This release includes breaking changes related to Typesense/Search functionality. Please review the [migration guide](/changelog/v11-migration) before upgrading. ## Search with metadata We have improved searching transactions, balances, and identities with metadata. Use your own tags, customer IDs, categories, or nested fields to filter and retrieve exactly what you need, making it easier to organize data around your business context. ## Fixes & improvements 1. Insufficient balance checks now include inflight transactions, giving a more accurate view of available funds and helping prevent unintentional overdrafts. 2. Improved handling of inflight transactions during commit and void operations in high-concurrency environments. 3. The system now properly excludes rejected inflight transactions from commit operations to prevent errors. 4. Enhanced search performance and memory usage optimizations. 5. Updated Typesense client library with security patches and improved API compatibility. 6. General bug fixes and performance improvements. ## Fixes & improvements 1. **Enhanced PII tokenization**: Strengthened PII tokenization functionality to properly handle international characters, emojis, and special symbols without corruption during tokenization and detokenization processes. ## Fixes & improvements 1. **Fixed telemetry configuration bug**: Resolved issue where `BLNK_ENABLE_TELEMETRY=false` would still default to enabled. Telemetry and observability concerns are now properly separated with independent feature flags. 2. **Enhanced transaction concurrency**: Introduced robust locking mechanism for inflight transactions to prevent race conditions during commit and void operations, ensuring data consistency in high-concurrency scenarios. 3. **Improved transaction reference management**: Updated transaction references to use UUID suffixes for unique identification instead of simple "-commit" suffixes, providing better transaction traceability and preventing reference collisions. 4. **Enhanced error handling**: Improved error handling for "reference already used" errors to provide better transaction flow management. 5. **Added TypeSense DNS validation**: Enhanced indexing reliability by ensuring TypeSense operations only occur when properly configured, preventing potential indexing failures. 6. **Added connection pooling support**: Improved database performance and reliability through enhanced connection management. ## Redis TLS verification control The Redis configuration now supports a `skip_tls_verify` option. This allows you to connect to Redis instances with self-signed or untrusted certificates by skipping TLS certificate verification. **Example:** ```json theme={"system"} { "redis": { "dns": "rediss://:@:", "skip_tls_verify": false } } ``` [See configuration docs →](/advanced/configuration/data-stores#redis-configuration) ## Bulk transaction queue processing Bulk transactions now use the queue system by default for improved performance and reliability. Learn more: [Why we use queueing](/guides/concurrency#queuing-transactions). 1. Bulk transactions default to `skip_queue: false`. 2. Users can add `"skip_queue": true` to bypass the queue for immediate processing. ## Other improvements 1. Enhanced balance creation to prevent duplicate entries with the same indicator and currency. ## Fixes & improvements 1. **Security**: Added authentication bypass for `/health` endpoint to enable health checks without authentication. ## New features This release brings significant improvements to transaction processing, balance management, and system reliability with enhanced locking mechanisms and better webhook handling. 1. **GetBalanceByIndicator functionality**\ You can now retrieve balances using custom indicators, providing more flexible balance querying capabilities for your applications. 2. **Zero-amount transaction handling**\ The system now properly handles zero-amount transactions, ensuring consistent behavior across all transaction types and edge cases. 3. **Update balance identity endpoint**\ Added new endpoint to update the identity associated with a balance, allowing for better customer data management and account linking. 4. **Kubernetes deployment support**\ Added comprehensive Kubernetes manifests for streamlined deployment and management in containerized environments. ## Major improvements 1. Enhanced transaction precision for better handling of large financial values. 2. Refunding transactions is now idempotent: you can no longer refund a transaction more than once. 3. Improved balance creation reliability to prevent race conditions. 4. Enhanced commit functionality using queue system for better transaction flow management. 5. Improved webhook processing for better reliability. 6. Enhanced webhook delivery consistency. 7. Improved webhook error handling and retry mechanisms. 8. Added comprehensive [health check endpoints](/reference/get-health) for better system monitoring. 9. Added [queue monitoring port](/advanced/monitoring-port) for real-time queue metrics and dashboard monitoring. 10. Enhanced system observability with improved logging and status tracking. 11. Better configuration handling for transaction lock duration. 12. Fixed API key revocation to properly bind to owner ID, preventing unauthorized access bypass. 13. Enhanced reconciliation testing framework for better financial accuracy validation. 14. Improved bulk transaction cleanup processes. 15. Enhanced Docker workflow with multi-platform support for broader deployment compatibility. 16. Streamlined deployment processes with better containerization. ## Bug fixes & improvements 1. General bug fixes and improvements. ## Granular access controls You can now create API keys with specific permissions to enforce granular access control in your ledger. ```json theme={"system"} { "name": "Balance Management", "owner": "balance_management", "scopes": ["balances:write", "search:write"], "expires_at": "2026-03-11T00:00:00Z" } ``` [Set your API keys →](/api-keys/overview) ## Overdraft limits Set a maximum overdraft threshold using the `overdraft_limit` parameter when you enable overdrafts for a transaction: ```json {10} theme={"system"} { "amount": 400.23, "precision": 100, "currency": "USD", "reference": "ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", "source": "bln_546df224-d7a8-4a20-8e27-399938cb3ead", "destination": "bln_8de42141-059f-42ea-9340-00394285620a", "description": "Wallet funding", "allow_overdraft": true, "overdraft_limit": 500 } ``` For example, setting `overdraft_limit` to 500.00 ensures the balance does not drop below -\$500.00, providing a safeguard against excessive overdrafts. [Set Overdraft Limits →](/transactions/overdrafts#setting-overdraft-limits) ## Use historical balances without balance snapshots You now have the option to bypass balance snapshots when utilizing the [historical balances](/balances/historical-balances#bypassing-balance-snapshots) feature, allowing your balances to be reconstructed with their transactions only. To do this, include the query parameter `from_source=true` in your request URL: ``` GET https://YOUR_BLNK_INSTANCE_URL/balances/{balance_id}/at?timestamp={timestamp}&from_source=true ``` ## Apply precision with `precise_amount` Accurately record amounts exceeding 15 digits with full precision using the `precise_amount` field: 1. Convert the amount to its smallest unit. 2. Enter this value directly into the `precise_amount` field of your request. 3. Include the corresponding `precision` value. ```json Example {2} theme={"system"} { "precise_amount": 189207535698279000, "precision": 1000000000000000000, "currency": "ETH", ... } ``` [Applying precision with `precise_amount` →](/transactions/precision#option-2-using-the-precise-amount-field) ## Balance reconstruction Correct ledger discrepancies by recalculating your balance directly from transactions. To use this feature, [update your balance metadata](/metadata/update-metadata) with the following key: ```json theme={"system"} { "meta_data": { "BLNK_RUN_RECONCILIATION": "SOURCE" } } ``` See [How Balance Reconstruction Works](/advanced/balance-reconstruction) for more details. ## Instant reconciliation You can now instantly match transactions and mark them as reconciled, with reconciliation details stored in the transaction's metadata for easy tracking. See [Instant Reconciliation](/reconciliations/overview#option-2-instant-reconciliation) for more details. ## Other improvements 1. Added database protections to prevent unauthorized changes to transactions and balance snapshots. 2. Introduced a transaction journal featuring a comprehensive audit log to track all activities within the transactions table. 3. Enabled metadata updates for multiple transactions using the `parent_transaction` ID, 4. Upgraded Redis connectivity with TLS support. 5. General bug fixes and performance improvements ## Bug fixes & improvements 1. General bug fixes and improvements. ## Bulk transactions The Bulk Transaction API enables you to process multiple transactions within a single request. It offers two processing options: atomic processing, where all transactions either succeed or fail as a unit, and independent processing, where each transaction is handled separately. Additionally, the API supports asynchronous processing to efficiently manage large batches of transactions. [Bulk Transactions →](/transactions/bulk-transactions) ## Backdated transactions Introducing the `effective_date` feature for recording transactions with a financial date different from their system entry date. This capability ensures seamless [migration](/guides/migration), accurate financial reporting, [reconciliation](/reconciliations/overview), and [historical balance](/balances/historical-balances) calculations. [Backdated Transactions →](/transactions/backdated-transactions) ## PII tokenization We've introduced a PII tokenization feature allowing you to replace sensitive customer data with non-sensitive tokens while maintaining the ability to use the data for business operations. This enhances security, reduces compliance scope, and maintains full functionality within your applications. [PII Tokenization →](/identities/pii-tokenization) ## Bug fixes & improvements 1. General bug fixes and improvements. 2. Added environment variables for Blnk configuration parameters. ## Bug fixes & improvements 1. General bug fixes and improvements. ## Balance snapshots & historical balances Introduced the Balance Snapshots feature, enabling users to retrieve historical balance data at any specific point in time. This enhances accurate financial reporting and analysis while maintaining efficient storage and retrieval mechanisms for optimal performance and scalability. [Balance Snapshots →](/balances/balance-snapshots) [Historical Balances →](/balances/historical-balances) ## New configuration for handling insufficient funds Added configuration parameters to control how the ledger processes transactions when funds are insufficient. 1. `insufficient_fund_retries`: Determines whether the ledger should automatically retry transactions that fail due to insufficient funds. 2. `max_retry_attempts`: Specifies the maximum number of retry attempts before the transaction is rejected. ## Added queued balances [Queued balances](/balances/introduction#queued-balances) help estimate the available or expected balance of a ledger. Unlike [inflight balances](/transactions/inflight/creating-inflight#inflight-balance-tracking), which require certain conditions to be met (commit or void) before being applied, queued balances simply represent the total amount of transactions waiting in the queue, regardless of conditions. To view the queued balances of a ledger balance, call the [Get Balance](/reference/get-balance) endpoint: ``` GET http://YOUR_BLNK_INSTANCE_URL/balances/{balance_id}?with_queued=true ``` ```json Response {9-10} theme={"system"} { "balance": 0, "version": 0, "inflight_balance": 0, "credit_balance": 0, "inflight_credit_balance": 0, "debit_balance": 0, "inflight_debit_balance": 0, "queued_credit_balance": 0, "queued_debit_balance": 0, "ledger_id": "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc", "identity_id": "", "balance_id": "bln_e39a239a-a6ca-4509-b0d9-29dcc5630f8a", "indicator": "", "currency": "USD", "created_at": "2024-07-05T08:13:18.882616461Z", "meta_data": { "customer_internal_id": "1234", "customer_name": "Jerry" } } ``` ## Direct transaction processing (skip queue) Added `skip_queue` option to allow transactions to bypass the queue system and process immediately. When enabled: 1. Transactions act directly on balances without entering the queued state 2. System maintains data consistency through Redis distributed locks 3. Optimistic locking at database level ensures transaction integrity To implement, add `"skip_queue": true` to your transaction request body when posting a transaction to your ledger. ```json {4} theme={"system"} { ... "inflight": true, "skip_queue": true } ``` ## Updates to transactions The `QUEUED` state is now stored in your Ledger to enhance organisation and traceability in your financial data. When a queued transaction is processed, we maintain a clear link to its origin: 1. Each queued transaction maintains two connections to its parent: 1. Direct link via `parent_transaction` field 2. Modified reference with `_q` suffix This ensures: 1. Clear audit trail of transaction flow 2. Easy tracking of transaction history 3. Prevention of reference collisions 4. Ability to trace complete transaction lifecycle Recording, sources, destinations, and statuses. ## Configuration file Blnk Finance now provides granular control over transaction and queue behaviors. This allows you to fine-tune system performance and behavior according to your needs. blnk.json options and environment variables. ## Metadata update The metadata update feature allows you to attach and modify additional information to different financial items (ledger, balance, transaction or identities) without altering their core attributes. This provides flexibility for storing custom data, tags, or references. ## Bug fixes & improvements 1. Enhanced distribution calculations with rounding support 2. Improved transaction processing and metadata handling 3. Enhanced configuration options for transaction and queue management 4. General bug fixes and improvements ## Bug fixes & improvements 1. General bug fixes and improvements. ## Bug fixes & improvements 1. Improved URL parsing for managed and Docker Redis connections. ## Bug fixes & improvements 1. `indicator` not returning for [internal balances](/balances/internal-balances) on the `GET /balances` endpoint is now fixed. ## Bug fixes & improvements 1. General bug fixes and improvements. ## Bug fixes & improvements 1. General bug fixes and improvements. ## Bug fixes & improvements 1. Improved transaction processing speed through optimized reference checking. ## Reconciliation We're excited to announce Reconciliation, a powerful new module that helps developers seamlessly compare and reconcile external records with your Blnk Ledger. This feature ensures data consistency, simplifies auditing processes, and reduces errors by automatically flagging mismatches. To learn more: [Reconciliation overview](/reconciliations/overview). ## Parent transactions We added `parent_transaction` for transactions with parent ids, e.g., inflight transactions, multiple sources/destinations, scheduled transactions, etc. For example, when an inflight transaction is committed, a new transaction is created in your ledger. This new transaction includes the `parent_transaction` field, which references the original inflight transaction. Other related improvements include: 1. Added pagination support for fetching transactions by `parent_transaction`. ## Bug fixes & improvements 1. Add identity and reconciliation collections to Search. 2. Added new tools for system monitoring to provide better visibility and faster detection of performance issues. 3. Added rate-limiting to ensure fair usage and maintain system reliability during high traffic periods. 4. Resolved issues with backups to Amazon S3. 5. Improved error handling to deliver clearer feedback and make debugging more efficient. ## Bug fixes & improvements 1. Fixed dates not being returned correctly. 2. Improved Search 3. `created_at` is now set before queuing transactions. ## Bug fixes & improvements 1. Added `precision` to balance monitor. 2. Added restart policy to migration. 3. Directus service and `.env` parameters added in **docker-compose** by [vebera](https://github.com/vebera). ## Rates We've introduced Rates, a feature that allows you to record transactions between balances of different currencies. ```json Applying rates {5} theme={"system"} { ... "source": "bln_28edb3e5-c168-4127-a1c4-16274e7a28d3", "destination": "bln_ebcd230f-6265-4d4a-a4ca-45974c47f746", "rate": 0.92 } ``` ## Bug fixes & improvements 1. Added `inflight_expiry_date` for specifying when an inflight transaction should expire and be automatically voided. 2. Added new balance fields and `operator` field to balance monitors. 3. You can now only commit up to the total amount of the original inflight transaction. ## Added search Search gives you full control and flexibility over how you fetch your transaction data from your Blnk Ledger. You can use Search to retrieve any data - ledgers, ledger balances, and transactions - from your Blnk Ledger. ## Transaction precision `precision` is now a transaction parameter. Blnk uses `precision` to convert transaction amounts to the lowest unit possible, e.g., converting naira to kobo, to ensure that your balances are correctly computed. The lowest unit for an asset class is a value that has no decimal point regardless of what amount is passed. ## Partial commits for inflight transactions You can now commit transactions in `INFLIGHT` in bits, i.e., you can release the amount being held by your system in batches based on the conditions you've set for the transaction or participating balance. ## Bug fixes & improvements 1. Fixed typos in the payloads for `inflight` and `allow_overdraft`. 2. General bug and performance improvements. ## Multiple sources & destinations You can now process transactions from [multiple sources](/transactions/multiple-sources) to one destination or from one source to [multiple destinations](/transactions/multiple-destinations). Multiple sources/destinations help you link similar transactions into one single request payload and making the process a lot more efficient for you. ## Inflight transactions We've introduced Inflight Transactions, a feature that allows you to hold a transaction until specific conditions are met. ```json Enable inflight {7} theme={"system"} { "amount": 12992.12, "reference": "ref_1234", "source": "bln_28edb3e5-c168-4127-a1c4-16274e7a28d3", "destination": "@World", "description": "Card payment", "inflight": true } ``` ## Overdrafts With the new Overdrafts feature, you can process transactions even if the ledger balance doesn't have sufficient funds. When overdraft is enabled, your balance can go negative, signifying that more money has been spent than received. ```json Enable overdraft theme={"system"} { "allow_overdraft": true } ``` ## Enhanced concurrency control Improved transaction processing to ensure that multiple transactions can be seamlessly processed simultaneously at scale. Learn more: [How Blnk handles concurrency](/resources/concurrency). ## Bug fixes & improvements 1. The `precision` value for a ledger balance is now automatically applied to the amounts of all transactions that the balance is part of. This ensures that your balance is correctly computed and no amount is lost to approximation. 2. General bug and performance improvement. ## Welcome to our first release Blnk Core is live and available for download. Fintech developers now have access to an open-source ledger database (a reliable source of truth) and fintech backend infrastructure for building their financial products at scale. ## Ledgers and ledger balances You can now model your customers' accounts, wallets, and all kinds of balances with our ledger and ledger balances features. You can also implement [balance monitoring](/balances/balance-monitoring) in one place. You can also represent your organization's chart of accounts in your Ledger with our [General Ledger](/ledgers/general-ledger) feature. ## Transactions You can accurately record and track transactions between balances in your Ledger. Blnk transactions feature also allows you to model and implement complex money movement within your system. You can also toggle on [refunds](/transactions/refunds), and [scheduling](/transactions/scheduling). ## Identities You can create and manage identities for your customers (individuals and organizations) within your Ledger. Learn more: [Identities](/identities/introduction) ## Advanced 1. Added https configuration for running your Blnk server in [secure mode](/advanced/enable-https). 2. Added k6 for [load testing](/advanced/load-testing). *** # Go SDK Source: https://docs.blnkfinance.com/changelog/blnk-go Latest releases, fixes, and improvements to the Blnk Go SDK. `blnk-go@v1.3.0` covers Blnk Core **0.15.0**. ### Identities Delete identities from the SDK when you need to remove a customer or organization from your ledger. ### Balance monitors Remove balance monitors you no longer need. Blnk stops checking thresholds and sending webhooks for them. ### Reconciliation Start a reconciliation run and get back a reconciliation ID. Check final status with webhooks or `Reconciliation.Get`. ### Transactions Bulk create supports up to **10,000** transactions per request (`MaxBulkCreateItems`). ### Error handling Core returns structured error codes. The SDK parses them into `ApiErrorResponse.ErrorDetail` so you can branch on `ErrorDetail.Code` without parsing message text. ### Before you upgrade Note the following breaking changes: * `Reconciliation.Run` now returns `StartReconciliationResponse` with only `reconciliation_id`. Use webhooks or `Reconciliation.Get` for final status. * Branch on `ErrorDetail.Code` for Core failures. Do not parse error message text. * Requires Go 1.22 or later. * Fully backward compatible with v1.2.0 except the `Reconciliation.Run` return type noted above. ### Reconciliation Run instant reconciliation, fetch a run by ID, and update or delete matching rules. ### Identities and search Filter identities from the SDK. Search the `identities` collection in Typesense. `identity_id` is optional on create. ### Transactions Read transaction lineage. Recover stuck queue items. Pass `skip_queue` on inflight commit or void, bulk inflight updates, and refunds. Use `precise_distribution` on split legs. ### Balances Create balances with fund lineage tracking. Read balances with `from_source`. ### Client Configurable retry strategy for transient failures. ### Before you upgrade * Requires Go 1.22 or later. * Fully backward compatible with v1.1.0. ### Transactions Bulk create transactions. Fetch a transaction by reference. Bulk commit or void up to 100 inflight transactions in one call, with support for partial commits. ### Balances Read fund lineage for a balance. Link a balance to an identity. Capture balance snapshots for historical lookups. ### Ledgers Rename a ledger without changing its ID, balances, or transaction history. ### Before you upgrade Note the following breaking changes: * Requires Go 1.22 or later. * Fully backward compatible with v1.0.1. ### What's in the SDK Create, list, and fetch ledgers. Create and read balances, including by indicator and at a point in time. Create, fetch, commit, void, and refund transactions. Manage balance monitors. Create, fetch, list, and update identities. Upload reconciliation data, create matching rules, and start runs. Search Typesense. Update metadata on any entity. ### Atomic transactions Mark a parent transaction as atomic when you create it. Set `Atomic: true` on `ParentTransaction`. *** # Java SDK Source: https://docs.blnkfinance.com/changelog/blnk-java Latest releases, fixes, and improvements to the Blnk Java SDK. `com.blnkfinance:blnk-java:1.3.0` covers Blnk Core **0.15.0**. Initial public release of the Java SDK with full parity against the TypeScript SDK. ### Coverage Ledgers, ledger balances (including monitors, snapshots, historical balances, and lineage), transactions (bulk, inflight, refunds, queue recovery), reconciliation, identities and PII tokenization, search and reindex, metadata, hooks, API keys, and system health. ### Client Sync client built on the JDK `java.net.http.HttpClient` and Jackson, with `Blnk.init` / `BlnkClientOptions.builder()`, configurable timeouts, GET-only retries, structured `ApiResponse` envelopes, and redacted logging. Services use camelCase accessors such as `transactions().create()` and fluent request builders in `com.blnkfinance.blnk.types`. ### Core 0.15.0 Structured error codes on `response.error().code()`. Inflight commit and void run through the queue by default; pass `skipQueue` for synchronous results. ### Before you upgrade Core Note the following breaking changes in Core 0.15.0: * Transaction and balance responses no longer include `rate` or `currency_multiplier`. * Branch on `response.error().code()` for Core failures. Do not parse error message text. # Python SDK Source: https://docs.blnkfinance.com/changelog/blnk-py Latest releases, fixes, and improvements to the Blnk Python SDK. `blnk-python@1.3.0` covers Blnk Core **0.15.0**. Initial public release of the Python SDK with full parity against the TypeScript SDK. ### Coverage Ledgers, ledger balances (including monitors, snapshots, historical balances, and lineage), transactions (bulk, inflight, refunds, queue recovery), reconciliation, identities and PII tokenization, search and reindex, metadata, hooks, API keys, and system health. ### Client Sync client built on `requests` with configurable timeouts, GET-only retries, structured `ApiResponse` envelopes, and redacted logging. ### Core 0.15.0 Structured error codes on `response.error.code`. Inflight commit and void run through the queue by default; pass `skip_queue` for synchronous results. ### Before you upgrade Core Note the following breaking changes in Core 0.15.0: * Transaction and balance responses no longer include `rate` or `currency_multiplier`. * Branch on `response.error.code` for Core failures. Do not parse error message text. *** # TypeScript SDK Source: https://docs.blnkfinance.com/changelog/blnk-ts Latest releases, fixes, and improvements to the Blnk TypeScript SDK. `blnk-typescript@1.3.0` covers Blnk Core **0.15.0**. ### Identities Delete identities from the SDK when you need to remove a customer or organization from your ledger. ### Balance monitors Remove balance monitors you no longer need. Blnk stops checking thresholds and sending webhooks for them. ### Reconciliation Start a reconciliation run and get back a reconciliation ID. Check final status with webhooks or `Reconciliation.get`. ### Inflight Commit or void inflight transactions from the SDK. On Core 0.15.0, these run through the queue by default. Pass `skip_queue` when you need an immediate applied or void result. ### Transactions Bulk create supports up to **10,000** transactions per request. ### Error handling Core returns structured error codes. The SDK puts them on the response so you can handle failures without parsing message text. ### Before you upgrade Note the following breaking changes: * Transaction and balance responses no longer include `rate` or `currency_multiplier`. * Branch on `response.error?.code` for Core failures. Do not parse error message text. `blnk-typescript@1.2.0` covers the rest of Core (up until `0.14.5`). ### Search Search via the DB or Typesense, start a reindex, and check reindex progress from the SDK. The `identities` collection is also included. ### Reconciliation Upload external data, create and update matching rules, start a run, check status, or reconcile instantly without writing HTTP calls from scratch. ### Identities Create, fetch, list, and update identities. Tokenize and detokenize PII, list what's tokenized, and supply your own `identity_id` or ISO 8601 `dob` on create when you need to. ### Metadata, hooks, and API keys Attach metadata to ledgers, balances, transactions, and identities. Register and manage transaction hooks. Create, list, and revoke scoped API keys. ### HTTP client The SDK now uses native `fetch`, so you need Node.js 18 or later. Timeouts and retries are configurable. Retries apply to idempotent `GET` requests only. Logs redact sensitive fields, empty API keys no longer send `X-Blnk-Key`, and `204` responses no longer fail JSON parsing. ### Before you upgrade Note the following breaking changes: * You'll need Node.js 18 or later. * The default HTTP timeout is 10 seconds. * `Identity.create` requires only `identity_type`. Everything else is optional. ### Transactions Fetch a transaction by ID or reference. Read lineage. Commit or void inflight transactions, refund, bulk create, bulk commit or void inflight batches, and recover stuck queue items. ### Balances Read a balance by ID (including from source), by indicator, at a point in time, or with lineage. Link a balance to an identity. Trigger balance snapshots. ### Ledgers and system Update ledger names. Check Core health with `System.health()`. ### Before you upgrade * Transaction response types now match Core. If you typed against `CreateTransactionResponse` or `BulkTransactionResponse`, update those types. * Client-side validation is stricter for dates, amounts, and split transaction legs. Fix invalid payloads before retrying. ### What's in the SDK Create ledgers, balances, and transactions. Manage balance monitors. Commit, void, refund, and bulk-create transactions. ### Fixes Updated SDK and example dependencies to patch known security vulnerabilities. *** # Blnk Watch Source: https://docs.blnkfinance.com/changelog/blnk-watch Latest releases, fixes, and improvements to Blnk Watch. ## Time-based rules Rules that use time functions now evaluate correctly. You can write conditions such as `hour_of_day(created_at) >= 22` or `day_of_week(created_at) in ("Saturday", "Sunday")` and Watch applies them using the transaction's `created_at` timestamp. Time functions also support `in` and `not_in` for lists of hours, weekdays, or day names. [Setting conditions →](/watch/rules/setting-conditions#time-based-functions) · [Time-based examples →](/watch/rules/examples/time-based-conditions) ## Previous transaction rules The `previous_transaction()` function now works as documented. Watch looks back from the current transaction's `created_at` and checks whether a matching transaction exists in that window. You can match on table fields such as `source` and `status`, or metadata paths such as `meta_data.status`. [Setting conditions →](/watch/rules/setting-conditions#previous-transaction-function) · [Compound examples →](/watch/rules/examples/compound-conditions) ## Private HTTPS rule repositories Watch can clone and pull private rule repos over HTTPS with `WATCH_SCRIPT_GIT_USERNAME` and `WATCH_SCRIPT_GIT_TOKEN`. Username defaults to `x-access-token` when unset. Credentials stay out of logs and Git status responses. [Configuration →](/watch/configuration#rule-source-configuration) · [License configuration →](/cloud/start/license/configuration) ## Rule name on verdicts Each entry in `dsl_verdicts` now includes the rule `name` alongside `rule_id`, `verdict`, `score`, and `reason`. Alert webhook payloads carry the same shape. [Defining verdicts →](/watch/rules/defining-verdicts#how-consolidation-works) · [Get verdict →](/watch/reference/get-verdict) ## Install Blnk Watch with one command v0.1.2 is the first release with prebuilt binaries. Install on Linux or macOS (Apple Silicon) with: ```bash wrap theme={"system"} curl -fsSL https://blnkfinance.xyz/install/watch | bash ``` ## Welcome to our first release Blnk Watch is live. You can monitor ledger transactions with custom risk rules, get a verdict for each transaction, and enforce limits, fraud checks, and compliance controls without running heavy infrastructure. [Get started with Blnk Watch →](/watch/quick-start) ## WatchScript rules Write rules in WatchScript (`.ws`), a domain-specific language for transaction monitoring. Define conditions on amount, source, destination, metadata, aggregates, and time windows, then set a verdict when a rule matches. [Setting conditions →](/watch/rules/setting-conditions) · [Defining verdicts →](/watch/rules/defining-verdicts) · [Rule structure →](/watch/rules/rule-structure) ## Transaction evaluation Send transactions to Watch for real-time evaluation. Watch returns a consolidated risk assessment with a verdict such as `allow`, `review`, or `block`, plus scores and reasons from each triggered rule. [How Watch works →](/watch/mental-model) · [Inject transaction →](/watch/reference/inject-transaction) · [Get verdict →](/watch/reference/get-verdict) ## Blnk Core integration Connect Watch to Blnk Core to evaluate transactions as they are recorded. Register a webhook so new transactions flow into Watch automatically, or sync historical transactions from your Core database. [Core integration →](/watch/integration) · [Blnk webhook →](/watch/reference/blnk-webhook) ## Rule management and deployment Store rules in a Git repository and let Watch sync them on a schedule. Compile instructions through the API, deploy the binary, and configure sync with environment variables. [Deploy Watch →](/watch/deployment) · [Configuration →](/watch/configuration) · [Git sync →](/watch/reference/sync-git-repository) *** # 0.11.0 Migration Guide Source: https://docs.blnkfinance.com/changelog/v11-migration Migration guide for upgrading from Blnk v0.10.x to v0.11.0, covering Typesense upgrades and breaking changes. This migration guide covers the breaking changes introduced in Blnk v0.11.0, specifically related to Typesense/Search functionality. The main change involves upgrading the Typesense server from version 0.23.x to 0.24.x, which requires updating your Docker configuration. *** ## Breaking changes summary * **From:** Typesense 0.23.x * **To:** Typesense 0.24.x * **Impact:** Breaking changes in API compatibility and schema handling *** ## Migration steps Update your Typesense server version in your Docker configuration files. 1. **For Production (`docker-compose.yaml`)** ```yaml theme={"system"} # Before (v0.10.x and lower) typesense: image: typesense/typesense:0.23.1 # After (v0.11.0) typesense: image: typesense/typesense:0.24.0 ``` 2. **For Development (`docker-compose.dev.yaml`)** ```yaml theme={"system"} # Before (v0.10.x and lower) typesense: image: typesense/typesense:0.23.1 # After (v0.11.0) typesense: image: typesense/typesense:0.24.0 ``` 3. **For Kubernetes (`infrastructure/k8s-manifests/typesense-deployment.yaml`)** ```yaml theme={"system"} # Before (v0.10.x and lower) containers: - image: typesense/typesense:0.23.1 # After (v0.11.0) containers: - image: typesense/typesense:0.24.0 ``` The new version includes automatic schema migration capabilities. When you start Blnk v0.11.0, it will automatically: 1. **Detect existing collections** in your Typesense instance. 2. **Compare schemas** between your current collections and the latest schema definitions. 3. **Add new fields** to existing collections without data loss. 4. **Create missing collections** with the latest schema. **Collections affected:** * `ledgers`. * `balances`. * `transactions`. * `reconciliations`. * `identities`. 1. **Ledgers collection:** * Enhanced metadata handling with object type support. * Improved nested field support. 2. **Balances collection:** * All balance fields now use `string` type for precise decimal handling. * Enhanced metadata object support. * Improved nested field capabilities. 3. **Transactions collection:** * `precise_amount` field uses `string` type for exact decimal precision. * Enhanced metadata object support. * Improved nested field capabilities. 4. **Reconciliations collection:** * No breaking changes to existing fields. * Enhanced performance optimizations. 5. **Identities collection:** * Enhanced metadata object support. * Improved nested field capabilities. Start your updated Blnk instance. The system will automatically handle data migration including: 1. **Large number conversion:** Automatically converts `big.Int` values to strings for Typesense compatibility. 2. **Metadata normalization:** Handles metadata field normalization for object schemas. 3. **Time field normalization:** Ensures consistent timestamp formatting. No manual intervention is required for data migration - everything happens automatically. No changes to environment variables or configuration files are required. All existing configurations remain compatible. * Environment variables remain the same. * `blnk.json` configuration structure unchanged. * Search API endpoints unchanged. * Response formats remain compatible. **API Endpoints (No Changes Required):** ```bash theme={"system"} # Single collection search POST /search/{collection} ``` *** ## Testing checklist After migration, verify the following: * Typesense server starts successfully. * All collections are accessible. * Search functionality works correctly. * Data integrity is maintained. * API endpoints respond correctly. * No errors in application logs. *** ## Performance improvements Blnk v0.11.0 includes several performance improvements: 1. **Enhanced search performance:** Optimized query execution. 2. **Improved memory usage:** Better resource management. 3. **Faster schema updates:** Streamlined collection management. 4. **Better error handling:** More robust error recovery. This migration guide covers the breaking changes in Blnk v0.11.0. For additional features and improvements, refer to the [release notes](/changelog/blnk-core) *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # 0.12.0 Migration Guide Source: https://docs.blnkfinance.com/changelog/v12-migration Migration guide for upgrading to Blnk v0.12.0, covering API key hashing implementation and breaking changes. This migration guide covers the breaking changes introduced in Blnk v0.12.0, specifically related to API key security enhancements. The main change involves implementing SHA-256 hashing for API keys, which affects how API keys are stored and retrieved. *** ## Breaking changes summary * **From:** API keys stored in plain text, retrievable from list endpoint * **To:** API keys hashed using SHA-256, plain text only available during creation * **Impact:** Once upgraded, existing API keys will return authentication errors when used. You must recreate API keys after upgrading. *** ## What changed 1. **API key hashing**: API keys are now hashed using SHA-256 before being stored in the database. 2. **Plain text key availability**: The plain text key is **only** returned during API key creation (`POST /api-keys`). 3. **List endpoint behavior**: When listing API keys (`GET /api-keys?owner=`), the `key` field now returns the hashed version instead of the plain text key. 4. **Security enhancement**: Plain text keys cannot be retrieved after creation for security reasons. *** ## What still works 1. **Creating API keys**: Plain text key is returned during creation. 2. **Authentication**: The system automatically hashes incoming keys for comparison. 3. **API key management**: Listing, revoking, and checking status still work. **Existing API keys will fail authentication after upgrade.** You must recreate all API keys after upgrading to v0.12.0. Old API keys cannot be migrated because their plain text values are not stored. *** ## Migration steps Identify all places in your codebase where API keys are retrieved or used: 1. **Check for code that reads API keys from `GET /api-keys`** * Any code that expects to use the `key` field from list responses. * Automation scripts that retrieve keys after creation. * Migration scripts that read existing API keys. 2. **Identify API key storage locations** * Where you currently store API keys. * How you retrieve them for authentication. * Any caching mechanisms that rely on plain text keys. Code that retrieves API keys from the list endpoint and expects plain text values will break. The `key` field now returns hashed values. Ensure you store the plain text key immediately after creation: ```bash theme={"system"} # Create API key curl -X POST "http://localhost:5001/api-keys" \ -H "X-blnk-key: " \ -H "Content-Type: application/json" \ -d '{ "name": "My API Key", "owner": "my-owner-id", "scopes": ["ledgers:read", "balances:write"], "expires_at": "2026-12-31T23:59:59Z" }' ``` **Response:** ```json theme={"system"} { "api_key_id": "api_key_123...", "key": "g-nnlP_kfxOP42baBGFJ", // STORE THIS IMMEDIATELY! "name": "My API Key", "owner_id": "my-owner-id", ... } ``` The plain text `key` value is only returned once during creation. Store it securely immediately-you cannot retrieve it later. **Store the key securely:** * Environment variables. * Secret management systems (AWS Secrets Manager, HashiCorp Vault, etc.). * Secure configuration files (excluded from version control). Remove any code that expects plain text keys from the list endpoint: **Before (BROKEN):** ```javascript theme={"system"} // ❌ This will no longer work const response = await fetch('/api-keys?owner=my-owner'); const keys = await response.json(); const apiKey = keys[0].key; // This is now hashed, not plain text! // Using this for authentication will fail ``` **After (CORRECT):** ```javascript theme={"system"} // ✅ Store key immediately after creation const createResponse = await fetch('/api-keys', { method: 'POST', headers: { 'X-Blnk-Key': masterKey, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'My Key', owner: 'my-owner', scopes: ['*:*'], expires_at: '2026-12-31T23:59:59Z' }) }); const newKey = await createResponse.json(); const plainTextKey = newKey.key; // Store this securely! // ✅ Use stored key for authentication const authHeaders = { 'X-Blnk-Key': plainTextKey // Use the stored plain text key }; ``` **Critical:** Existing API keys will stop working after upgrading to v0.12.0. You must recreate all API keys before or immediately after upgrading. Before upgrading, or immediately after: 1. **Identify all existing API keys** that are currently in use. 2. **Create new API keys** with the same scopes and permissions. 3. **Store the new plain text keys** securely immediately after creation. 4. **Update your applications** to use the new keys. 5. **Test authentication** with the new keys. 6. **Revoke old API keys** once migration is complete. Old API keys cannot be migrated because their plain text values are not stored. You must create new API keys and update your applications to use them. Test that authentication works correctly: ```bash theme={"system"} # Test authentication with stored plain text key curl -X GET "http://localhost:5001/ledgers" \ -H "X-blnk-key: " ``` Authentication works seamlessly-the system automatically hashes incoming keys for comparison with stored hashes. *** ## Security benefits 1. **Secure storage**: API keys are stored as hashes, not plain text. 2. **Database protection**: Even if database is compromised, plain text keys cannot be extracted. 3. **Best practices**: Follows security best practices for credential storage. 4. **Reduced exposure risk**: Reduces risk of key exposure through logs or database dumps. *** ## Technical details * **Hashing algorithm**: SHA-256. * **Authentication**: System handles hashing automatically during authentication. * **Caching**: API key caching has been improved for better performance. * **Cache invalidation**: Occurs automatically when keys are revoked. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # v13.2 breaking change Source: https://docs.blnkfinance.com/changelog/v13-2-migration Migration guide for upgrading to Blnk v0.13.2, covering the unique reference database constraint on transactions.reference. This migration guide covers the breaking database change introduced in Blnk v0.13.2. Blnk now enforces a **unique index** on `transactions.reference` at the PostgreSQL layer. If you are upgrading from **v0.13.1 or earlier** and your ledger already contains transaction data, audit for duplicate references before you upgrade. *** ## Breaking changes summary * **From:** Non-unique index on `reference`; uniqueness enforced only in application logic (with gaps on some queued and bulk paths). * **To:** Unique index `idx_transactions_reference_unique` on `blnk.transactions(reference)`. * **Impact:** The database migration fails at startup if duplicate references already exist. After upgrade, duplicate references are rejected at the database layer, not only by application checks. *** ## What changed 1. **Unique reference constraint:** Every transaction `reference` must be unique in the database. If two rows share the same reference, the upgrade fails until you fix them. 2. **Queue recovery index:** The same release also improves recovery of transactions stuck in `QUEUED`. This does not change how you post transactions. See [Queue recovery](/advanced/queue-recovery). *** ## Migration steps Before upgrading, check whether your database already contains duplicate `reference` values: ```sql theme={"system"} SELECT reference, COUNT(*) AS duplicate_count FROM blnk.transactions WHERE reference IS NOT NULL AND reference <> '' GROUP BY reference HAVING COUNT(*) > 1 ORDER BY duplicate_count DESC, reference; ``` ```text Example output wrap theme={"system"} reference | duplicate_count -----------+--------------- ref_123 | 3 ref_abc | 2 (2 rows) ``` If this query returns no rows, you can proceed to upgrade. This query finds duplicate references, keeps the oldest row in each group unchanged, and renames every later row by appending `_dup_` plus its `transaction_id`. ```sql theme={"system"} UPDATE blnk.transactions t SET reference = t.reference || '_dup_' || t.transaction_id FROM ( SELECT transaction_id, ROW_NUMBER() OVER ( PARTITION BY reference ORDER BY created_at ASC ) AS row_num FROM blnk.transactions WHERE reference IS NOT NULL AND reference <> '' ) ranked WHERE t.transaction_id = ranked.transaction_id AND ranked.row_num > 1; ``` ```text Example output wrap theme={"system"} transaction_id | reference -------------------+--------------------------------- txn_f482a1b3-... | ref_123 txn_c5d9e2a1-... | ref_123_dup_txn_c5d9e2a1-... txn_a8b3c1d2-... | ref_123_dup_txn_a8b3c1d2-... (3 rows) ``` The earliest `ref_123` row stays as-is. The two later rows get unique references so the upgrade can proceed. Re-run the audit query from step 1 until it returns no rows. Deploy Blnk **v0.13.2** or a later release. Migrations run automatically on startup and apply `1770611011.sql`. The unique index ships in **v0.13.2**. Later releases (including all **v0.14.x** patches) inherit the same migration unchanged - none of them introduce this constraint separately. Confirm the unique index exists: ```sql theme={"system"} SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'blnk' AND tablename = 'transactions' AND indexname = 'idx_transactions_reference_unique'; ``` Optionally, post a transaction with a `reference` that already exists in your ledger. Blnk should reject the duplicate. The index is present and duplicate references are rejected. *** ## Technical details * **Migration file:** `1770611011.sql` * **Index name:** `idx_transactions_reference_unique` * **Legacy index:** Before 0.13.2, `reference` had a non-unique index (`idx_transactions_reference` from the initial schema). This migration guide covers the breaking change in Blnk v0.13.2. For other features in this release, such as Search via Database, refer to the [release notes](/changelog/blnk-core). *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # 0.14.0 Migration Guide Source: https://docs.blnkfinance.com/changelog/v14-migration Migration guide for upgrading to Blnk v0.14.0, covering the Update Metadata response field change from metadata to meta_data. This migration guide covers the breaking change introduced in Blnk v0.14.0 for the [Update Metadata](/reference/update-metadata) endpoint. The response now returns the field **`meta_data`** (with underscore) instead of the inconsistent **`metadata`**. If your code reads the update-metadata response by key, you need to handle this change. *** ## Breaking changes summary * **From:** Response contains `metadata` (inconsistent with request body and other APIs) * **To:** Response uses `meta_data` (consistent with request body and other APIs) * **Impact:** Code that reads the update-metadata response using the key `metadata` will no longer find the object. Use `meta_data` or support both for compatibility. *** ## What changed 1. **Update Metadata response:** `POST /{id}/metadata` responses now return the metadata object under the key **`meta_data`** only. 2. **Consistency:** This aligns the response with the request body parameter name (`meta_data`) and with how metadata is exposed elsewhere in the API. *** ## Recommended approach: Expect both keys To support Blnk versions before and after 0.14.0, read the metadata from the response using either `meta_data` or `metadata`, with `meta_data` taking precedence when present. ```typescript TypeScript wrap theme={"system"} const response = await fetch(`${baseUrl}/${resourceId}/metadata`, { method: 'POST', headers: { 'X-blnk-key': apiKey, 'Content-Type': 'application/json', }, body: JSON.stringify({ meta_data: { key: 'value' } }), }); const data = await response.json(); // Support both meta_data (0.14.0+) and metadata (pre-0.14.0) const metadata = data.meta_data ?? data.metadata ?? {}; ``` ```go Go wrap theme={"system"} var result struct { MetaData map[string]interface{} `json:"meta_data"` Metadata map[string]interface{} `json:"metadata"` // pre-0.14.0 } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { // handle error } metadata := result.MetaData if metadata == nil { metadata = result.Metadata } if metadata == nil { metadata = make(map[string]interface{}) } ``` ```python Python wrap theme={"system"} response = requests.post( f"{base_url}/{resource_id}/metadata", headers={"X-blnk-key": api_key, "Content-Type": "application/json"}, json={"meta_data": {"key": "value"}}, ) data = response.json() # Support both meta_data (0.14.0+) and metadata (pre-0.14.0) metadata = data.get("meta_data") or data.get("metadata") or {} ``` ```java Java wrap theme={"system"} HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(baseUrl + "/" + resourceId + "/metadata")) .header("X-blnk-key", apiKey) .header("Content-Type", "application/json") .POST(HttpRequest.BodyPublishers.ofString("{\"meta_data\":{\"key\":\"value\"}}")) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); ObjectMapper mapper = new ObjectMapper(); JsonNode data = mapper.readTree(response.body()); // Support both meta_data (0.14.0+) and metadata (pre-0.14.0) JsonNode metadata = data.hasNonNull("meta_data") ? data.get("meta_data") : (data.hasNonNull("metadata") ? data.get("metadata") : mapper.createObjectNode()); ``` ```php PHP theme={"system"} $response = $client->post("{$baseUrl}/{$resourceId}/metadata", [ 'headers' => [ 'X-blnk-key' => $apiKey, 'Content-Type' => 'application/json', ], 'json' => ['meta_data' => ['key' => 'value']], ]); $data = json_decode($response->getBody()->getContents(), true); // Support both meta_data (0.14.0+) and metadata (pre-0.14.0) $metadata = $data['meta_data'] ?? $data['metadata'] ?? []; ``` *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # 0.15.0 Migration Guide Source: https://docs.blnkfinance.com/changelog/v15-migration Migration guide for upgrading to v0.15.0, covering error codes, HTTP statuses, and bulk inflight commit/void. This guide covers the changes you need to review when upgrading to Blnk v0.15.0. Most integrations can upgrade without major changes. Review this guide carefully if your app: * Branches on HTTP status codes * Parses error message text * Expects inflight commit or void requests to return `APPLIED` or `VOID` immediately * Reads reconciliation status directly from the start response *** ## At a glance | Change | Who is affected? | What changed | | ---------------------------------------------- | -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Structured `error_detail` added | Most integrations are not affected | Error responses now include `error_detail.code`, `error_detail.message`, and `error_detail.details`. The existing `error` field is still returned. | | HTTP status codes corrected | Integrations that branch on old HTTP statuses | Some errors now return more specific statuses, such as `404`, `409`, `423`, and `500`. | | `NOT_FOUND:` message prefix removed | Integrations that parse error message text | Error messages no longer include the `NOT_FOUND:` prefix. | | Inflight commit and void are queued by default | Integrations that expect immediate `APPLIED` or `VOID` responses | Commit and void requests now return a queued response by default. | | Reconciliation start response changed | Integrations that read reconciliation status from the start response | Start endpoints return only `reconciliation_id`. Handle `reconciliation.completed` and `reconciliation.failed` webhooks for results. | | Per-endpoint item limits | Integrations that send large bulk or reconciliation requests | Bulk create and instant reconciliation: max **10,000** items per request. Bulk commit and void: max **100** transactions. | | Request and upload size caps | Integrations with large JSON bodies or file uploads | Configurable caps apply to JSON request bodies and multipart uploads. See [Server and security configuration](/advanced/configuration/server-security#request-and-payload-limits). | | Transaction hash chain (optional) | Operators who want ledger-wide tamper detection | New opt-in global hash chain; disabled by default. Enable in config and run `blnk verify-chain`. See [Transaction hashing](/transactions/hash). | *** ## Structured `error_detail` added Error responses now include a structured `error_detail` object. The existing `error` field is still returned, so existing integrations that read error continue to work. ```json wrap theme={"system"} { "error": "transaction not found", "error_detail": { "code": "TXN_NOT_FOUND", "message": "transaction not found", "details": {} } } ``` For new error handling logic, branch on `error_detail.code`. Do not branch on `error`, `errors`, or `error_detail.message`. These fields are human-readable and may change between releases. See [API error codes](/advanced/error-codes) documentation for the errors catalog. *** ## HTTP status codes corrected Several errors that previously returned `400` now return more specific HTTP statuses. The response body still includes `error` and `error_detail`. The main change is the HTTP status code returned with the response. Review any logic that: * Treats every failed request as `400` * Branches only on HTTP status codes * Does not read `error_detail.code` | Condition | Before | Now | | ------------------------------------------------------------------------ | ------ | ----------------------- | | Resource not found | 400 | 404 | | Duplicate transaction reference | 400 | 409 | | Inflight already committed or already voided | 400 | 409 | | Identity field already tokenized | 400 | 409 | | Tokenization disabled | 400 | 403 | | API key create or list request missing `owner` when using the master key | 401 | 400 | | Hook infrastructure failures | 400 | 500 | | Database backup failures | 400 | 500 | | Lock contention | 400 | 423 | | Unclassified internal failures | 400 | 500 (sanitized message) | *** ## `NOT_FOUND:` message prefix removed Error messages no longer include the `NOT_FOUND:` prefix. If your integration checks for this prefix, update it to use `error_detail.code`. ```javascript Before wrap theme={"system"} if (error.message.startsWith("NOT_FOUND:")) { // handle not found } ``` ```javascript Now wrap theme={"system"} if (body.error_detail?.code === "TXN_NOT_FOUND") { // handle not found } ``` This is safer because `error_detail.code` is stable. Message text is meant for display and may change between releases. *** ## Inflight commit and void are queued by default Single and bulk inflight commit and void requests now go through the queue by default. This means the action is processed asynchronously instead of being applied immediately in the request cycle. By default, the response returns the transaction with: ```json theme={"system"} { "transaction_id": "txn_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", "status": "INFLIGHT", "queued": true } ``` The transaction remains `INFLIGHT` until the worker processes the queued commit or void. A second queued commit or void for the same transaction returns `409 Conflict`. To keep the previous synchronous behavior, send `skip_queue: true` in the request body. Use this when your app needs the request to return an immediate `APPLIED` or `VOID` response. ```bash cURL wrap {6} theme={"system"} curl -X PUT "http://YOUR_BLNK_INSTANCE_URL/transactions/inflight/{transaction_id}" \ -H "X-Blnk-Key: " \ -H "Content-Type: application/json" \ -d '{ "status": "commit", "skip_queue": true }' ``` ```typescript TypeScript wrap {3} theme={"system"} const response = await blnk.Transactions.updateStatus('{transaction_id}', { status: 'commit', skip_queue: true, }); ``` ```go Go wrap {3} theme={"system"} transaction, resp, err := client.Transaction.Update("{transaction_id}", blnkgo.UpdateStatus{ Status: blnkgo.InflightStatusCommit, SkipQueue: true, }) ``` ```python Python wrap {3} theme={"system"} response = blnk.transactions.update_status("{transaction_id}", { "status": "commit", "skip_queue": True, }) ``` ```java Java wrap theme={"system"} ApiResponse response = blnk.transactions().updateStatus( "{transaction_id}", UpdateTransactionStatus.create() .status("commit") .skipQueue(true)); ``` *** ## Reconciliation response changed [Start reconciliation](/reference/start-reconciliation) and [Instant reconciliation](/reference/instant-reconciliation) no longer return run status, match counts, or timestamps in the start response. Both endpoints now return only `reconciliation_id`. Here's the new flow: 1. Start the reconciliation and save the returned `reconciliation_id`. 2. Handle `reconciliation.completed` or `reconciliation.failed` on your webhook endpoint. 3. Read status, match counts, and timestamps from the webhook payload. See [Reconciliations](/reconciliations/overview) and [Supported events](/webhooks/events#reconciliations). *** ## Request and item limits Blnk Core 0.15.0 adds per-endpoint item limits and configurable request size caps: | Endpoint | Max per request | | ------------------------------------------------------------------------------------------------------------- | ---------------------------- | | [Bulk transactions](/reference/bulk-transactions) | 10,000 transactions | | [Instant reconciliation](/reference/instant-reconciliation) | 10,000 external transactions | | [Bulk commit inflight](/reference/bulk-commit-inflight) / [Bulk void inflight](/reference/bulk-void-inflight) | 100 transactions | Configurable JSON body and upload size caps are documented in [Server and security configuration](/advanced/configuration/server-security#request-and-payload-limits). *** ## Recommended upgrade checklist Before upgrading to v0.15.0: * Replace message-text parsing with `error_detail.code`. * Review any logic that depends on HTTP status codes. * Update inflight commit and void flows to handle queued responses or use `skip_queue: true` to keep existing synchronous behavior. * Update reconciliation flows to handle `reconciliation.completed` and `reconciliation.failed` webhooks instead of reading status from the start response. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Balances Source: https://docs.blnkfinance.com/cli/balances Use the Blnk CLI to list, create, and update balances on your Core instance. Use the `balances` command to list monetary accounts, inspect one balance by ID, create balances on a ledger, and link balances to identities. Balances hold currency positions and appear as sources or destinations on transactions. | Command | Alias | Description | | :------ | :---- | :---------------------------------------------------- | | list | l | Browse balances in a table; add `--id` for one record | | create | c | Open a balance on a ledger via interactive prompts | | update | u | Link an existing balance to an identity | *** ## `list` Browse balances across your ledgers in a paginated table with amounts, credit/debit totals, and inflight columns. Use `--{field}` filters (for example `--currency`) to narrow results before you fetch one record with `list --id`. ```bash theme={"system"} blnk balances list [options] ``` **Options** | Option | Type | Default | Description | | :----------- | :------ | :------ | :---------------------------------------------------------------- | | `--{field}` | string | - | Filter by any indexed field (for example `--currency "USD,EUR"`). | | `-p, --page` | integer | `1` | Page number. | | `--per-page` | integer | `10` | Results per page. | | `-h, --help` | boolean | - | Show help for this command. | **Usage** Run the command to view a paginated table of balances: ```bash wrap theme={"system"} blnk balances list ``` Filter and paginate: ```bash wrap theme={"system"} blnk balances list --page 2 --per-page 3 blnk balances list --currency "USD,EUR" --indicator World ``` ```text 200 Success icon="circle-check" theme={"system"} ┌──────────────── ──────── ───────── ─────── ─────── ─────── ──────── ──────── ──────── ───────────────────────────┐ │ Balance ID │ Currency │ Indicator │ Balance │ Credit │ Debit │ Inflight │ Infl. Dr │ Infl. Cr │ Created At │ | ---------------- | -------- | --------- | ------- | ------- | ------- | -------- | -------- | -------- | --------------------------- | │ bln_01dab2b7... │ USDC │ N/A │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 2026-06-03T12:37:50.156041Z │ │ bln_964c5fbc... │ EUR │ N/A │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 2026-06-03T12:37:16.100793Z │ │ bln_d98d6524... │ USDC │ @Artur │ 124000 │ 124000 │ 0 │ 0 │ 0 │ 0 │ 2026-06-03T12:34:52.961135Z │ └──────────────── ──────── ───────── ─────── ─────── ─────── ──────── ──────── ──────── ───────────────────────────┘ 54 results found Page 1/18 ``` *** ## `list --id` Look up one balance by `balance_id` and print a readable summary of ledger link, identity, and balance breakdowns. Pass `--json` when you need the full API object for scripts or support tickets. ```bash theme={"system"} blnk balances list --id [options] ``` **Options** | Option | Type | Required | Description | | :----------- | :------ | :------- | :---------------------------------------------------------------------------- | | `--id` | string | Yes | Balance ID to fetch (for example `bln_01dab2b7-3ec7-4253-b302-edd947423883`). | | `--json` | boolean | No | Print the full API object as JSON instead of a formatted summary. | | `-h, --help` | boolean | No | Show help for this command. | **Usage** Run the command with a balance ID: ```bash wrap theme={"system"} blnk balances list --id bln_01dab2b7-3ec7-4253-b302-edd947423883 ``` ```text 200 Success icon="circle-check" theme={"system"} Balance details: balance_id: bln_01dab2b7-3ec7-4253-b302-edd947423883 ledger_id: ldg_feb41a88-02e0-46b1-813d-e60aba7fe0aa identity_id: idt_fa2e58ba-9826-4614-9ba2-02b62df8c4d0 currency: USDC balance: 0 debit_balance: 0 credit_balance: 0 inflight_balance: 0 inflight_debit_balance: 0 inflight_credit_balance: 0 created_at: 2026-06-03T12:37:50Z ``` ```text JSON icon="circle-check" theme={"system"} { "balance_id": "bln_01dab2b7-3ec7-4253-b302-edd947423883", "ledger_id": "ldg_feb41a88-02e0-46b1-813d-e60aba7fe0aa", "identity_id": "idt_fa2e58ba-9826-4614-9ba2-02b62df8c4d0", "currency": "USDC", "balance": 0, "credit_balance": 0, "debit_balance": 0, "inflight_balance": 0, "created_at": "2026-06-03T12:37:50.156041Z", "meta_data": {} } ``` *** ## `create` Open a new balance on a ledger through prompts for currency, ledger ID, and optional identity. Every balance belongs to exactly one ledger; you can link an identity now or later with `update`. ```bash theme={"system"} blnk balances create [options] ``` **Arguments** | Argument | Type | Required | Description | | :------------ | :----- | :------- | :------------------------------------------------------------------------------- | | `currency` | string | Yes | Balance currency. Prompt: `currency >` | | `ledger_id` | string | Yes | Ledger ID for the balance. Prompt: `ledger id >` | | `identity_id` | string | No | Optional identity to link. Prompt: `identity id >`. Press **Enter** to skip. | | `meta_data` | object | No | Optional metadata as JSON. Prompt: `metadata (json) >`. Press **Enter** to skip. | **Options** | Option | Type | Description | | :----------- | :------ | :-------------------------------------------------------------- | | `--json` | boolean | After a successful create, print the full API response as JSON. | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command: ```bash wrap theme={"system"} blnk balances create ``` The CLI prompts for each argument: ```text wrap theme={"system"} currency > ledger id > identity id > metadata (json) > ``` Press **Enter** at optional prompts to skip. ```text 200 Success icon="circle-check" theme={"system"} Balance created: balance_id: bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f ledger_id: ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc identity_id: N/A currency: USD created_at: 2026-05-23T17:07:49Z ``` ```text JSON icon="circle-check" theme={"system"} { "balance_id": "bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f", "ledger_id": "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc", "identity_id": "N/A", "currency": "USD", "created_at": "2026-05-23T17:07:49.876192Z", "meta_data": {} } ``` *** ## `update` Attach an identity to a balance that was created without one (or change the linked identity). Pass the balance ID on the command line; the CLI prompts for the `identity_id` to store on the record. ```bash theme={"system"} blnk balances update [options] ``` **Arguments** | Argument | Type | Required | Description | | :------------ | :----- | :------- | :--------------------------------------------------------------------------------------------------------- | | `balance-id` | string | Yes | Balance ID to update (for example `bln_01dab2b7-3ec7-4253-b302-edd947423883`). Passed on the command line. | | `identity_id` | string | Yes | Identity ID to link. | **Options** | Option | Type | Description | | :----------- | :------ | :-------------------------- | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command with a balance ID: ```bash wrap theme={"system"} blnk balances update bln_01dab2b7-3ec7-4253-b302-edd947423883 ``` The CLI prompts for the identity to link: ```text wrap theme={"system"} identity id > ``` ```text 200 Success icon="circle-check" theme={"system"} Balance identity updated: balance_id: bln_01dab2b7-3ec7-4253-b302-edd947423883 ledger_id: ldg_feb41a88-02e0-46b1-813d-e60aba7fe0aa identity_id: idt_fa2e58ba-9826-4614-9ba2-02b62df8c4d0 currency: USDC created_at: 2026-06-03T12:37:50Z ``` *** # Join community Source: https://docs.blnkfinance.com/cli/community Open the Blnk community Discord from the CLI. Open the Blnk Discord invite in your default browser when you want to ask questions, share feedback, or follow product updates with the team and other builders. ```bash theme={"system"} blnk community ``` **Usage** Run the command: ```bash wrap theme={"system"} blnk community ``` ```text 200 Success icon="circle-check" theme={"system"} Opening Blnk Community Discord Welcome! https://discord.gg/7WNv94zPpx ``` ```text wrap Couldn't open browser icon="circle-x" theme={"system"} error: Couldn't open browser. Please try again. ``` *** # Open docs Source: https://docs.blnkfinance.com/cli/docs Open Blnk developer documentation from the CLI. Jump from the terminal to the full Blnk developer docs in your default browser when you need guides or API reference beyond what `help` shows. ```bash theme={"system"} blnk docs ``` **Usage** Run the command: ```bash wrap theme={"system"} blnk docs ``` ```text 200 Success icon="circle-check" theme={"system"} Opening Blnk Docs Opened https://docs.blnkfinance.com ``` ```text wrap Couldn't open browser icon="circle-x" theme={"system"} error: Couldn't open browser. Please try again. ``` *** # Global flags Source: https://docs.blnkfinance.com/cli/global-flags Root-level flags and flags shared across many Blnk CLI commands. Global flags apply at the root of the CLI or recur on multiple commands. Command-specific flags are documented on each resource or additional command page. *** ## Global flags Blnk CLI supports the following global flags: | Flag | Type | Description | | :-------------- | :------ | :--------------------------------------------------------------------------------------- | | `-h, --help` | boolean | Show help for the current command.
Run `blnk [command] --help` for subcommand help. | | `-v, --version` | boolean | Print the CLI version and exit. | *** ## Shared resource flags These flags appear on many `list`, `create`, and fetch commands under **Resource commands**. | Flag | Type | Default | Description | | :----------- | :------ | :------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--json` | boolean | - | Print the full API response as JSON.
On `create`, prints JSON after a successful request.
On `list`, applies when fetching a single resource with `--id`. | | `--id` | string | - | Fetch a single resource by ID on `list` commands
(for example `--id ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc` for ledgers).
Without `--json`, the CLI prints a formatted summary;
with `--json`, it prints the full object. | | `-p, --page` | integer | `1` | Page number for paginated list results. | | `--per-page` | integer | `10` | Number of results per page for paginated lists. | | `--{field}` | string | - | Filter list results by any indexed field.
Pass the field name after `--` and the value after a space.
Quote comma-separated values for multiple matches,
for example `--currency "USD,EUR"`. | | `-h, --help` | boolean | - | Show help for the current subcommand. | For how output changes with these flags, see [CLI output formats](/cli/output-formats). *** # Help guide Source: https://docs.blnkfinance.com/cli/help Get help for Blnk CLI commands from the terminal. Pass `-h` or `--help` on any command to see usage, options, and examples (for example `blnk ledgers list --help`). Run `blnk --help` for top-level commands. *** ## `help` Look up usage, options, and examples without leaving the terminal. ```bash theme={"system"} blnk --help blnk [command] --help ``` **Usage** Run with no command, or pass `--help` at the root, to see top-level commands and global options: ```bash wrap theme={"system"} blnk --help ``` ```text Help guide icon="circle-check" expandable theme={"system"} The official command-line tool to interact with your Blnk Core. Usage: blnk [command] [options] Connection commands: connect, c Connect to a Blnk Core instance info, i Show current connection settings Resource commands: ledgers Make requests (create, list, etc.) on ledgers balances Make requests (create, list, etc.) on balances transactions Make requests (create, list, etc.) on transactions identities Make requests (create, list, etc.) on identities reconciliations Make requests (create, list, etc.) on reconciliations Other commands: community Chat with the Blnk team & other developers in our Discord docs Open Blnk developer documentation reindex Manage Typesense reindex jobs Options: -h, --help Show help information -v, --version Show CLI version Tip: Use "blnk [command] --help" for more information about a command. ``` See commands under a resource (for example `ledgers`): ```bash wrap theme={"system"} blnk ledgers --help ``` ```text Help guide icon="circle-check" theme={"system"} Description: Make requests (create, list, etc.) on ledgers. Usage: blnk ledgers [command] [options] Commands: list, l View all ledgers create, c Create a new ledger Options: -h, --help Show help information ``` See options and examples for a specific subcommand: ```bash wrap theme={"system"} blnk ledgers list --help ``` ```text Help guide icon="circle-check" expandable theme={"system"} Description: View a list of your ledgers. Usage: blnk ledgers list [options] Options: Single list: --id Get a ledger by ID --json Print full JSON output for single fetch Group list: -- Filter by any field (quote comma-separated values, e.g. --currency "USD,EUR") -p, --page Page number (default: 1, e.g. 2) --per-page Results per page (default: 10, e.g. 25) -h, --help Show help information Examples: $ blnk ledgers list $ blnk ledgers l --page 2 --per-page 20 $ blnk ledgers l --ledger-id ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc ``` *** # Identities Source: https://docs.blnkfinance.com/cli/identities Use the Blnk CLI to list and create identities on your Core instance. Use the `identities` command to list people and organizations on Core, inspect one profile by ID, and register new identities interactively. Link identities to balances when you need a customer or counterparty on a ledger. | Command | Alias | Description | | :------ | :---- | :------------------------------------------------------ | | list | l | Browse and filter identities; add `--id` for one record | | create | c | Register an individual or organization via prompts | *** ## `list` Search and page through individuals and organizations on Core. Filter on indexed profile fields (email, first name, last name, and others) to find the `identity_id` you will use when linking balances. ```bash theme={"system"} blnk identities list [options] ``` **Options** | Option | Type | Default | Description | | :----------- | :------ | :------ | :---------------------------------------------------------------------------- | | `--{field}` | string | - | Filter by any indexed field (for example `--email-address jane@example.com`). | | `-p, --page` | integer | `1` | Page number. | | `--per-page` | integer | `10` | Results per page. | | `-h, --help` | boolean | - | Show help for this command. | **Usage** Run the command to view a paginated table of identities: ```bash wrap theme={"system"} blnk identities list ``` Filter and paginate: ```bash wrap theme={"system"} blnk identities list --page 2 --per-page 20 blnk identities list --email-address jane@example.com blnk identities list --first-name Jane --last-name Doe ``` ```text 200 Success icon="circle-check" theme={"system"} ┌──────────────── ────────── ────────── ───────────────────────────────── ───────────────────────────┐ │ Identity ID │ First Name │ Last Name │ Email Address │ Created At │ | ---------------- | ---------- | ---------- | --------------------------------- | --------------------------- | │ idt_8ff119a1... │ N/A │ N/A │ contact@northwind-traders.example │ 2026-05-23T13:42:16.547035Z │ │ idt_9f21736f... │ N/A │ N/A │ accounts@pacific-rim.example │ 2026-05-23T13:42:15.344356Z │ │ idt_20e8d4f3... │ Wei │ Lin │ wei.lin@example.com │ 2026-05-23T13:42:14.258165Z │ └──────────────── ────────── ────────── ───────────────────────────────── ───────────────────────────┘ 20 results found Page 1/2 ``` *** ## `list --id` Fetch one identity by `identity_id` with type, contact fields, and timestamps in a summary block. Pass `--json` to include address fields, phone, and full `meta_data` in the output. ```bash theme={"system"} blnk identities list --id [options] ``` **Options** | Option | Type | Required | Description | | :----------- | :------ | :------- | :----------------------------------------------------------------------------- | | `--id` | string | Yes | Identity ID to fetch (for example `idt_8ff119a1-9103-487c-ae07-1449583fb126`). | | `--json` | boolean | No | Print the full API object as JSON instead of a formatted summary. | | `-h, --help` | boolean | No | Show help for this command. | **Usage** Run the command with an identity ID: ```bash wrap theme={"system"} blnk identities list --id idt_8ff119a1-9103-487c-ae07-1449583fb126 ``` ```text 200 Success icon="circle-check" theme={"system"} Identity details: identity_id: idt_8ff119a1-9103-487c-ae07-1449583fb126 identity_type: organization first_name: N/A last_name: N/A email_address: contact@northwind-traders.example created_at: 2026-05-23T13:42:16Z ``` ```text JSON icon="circle-check" theme={"system"} { "identity_id": "idt_8ff119a1-9103-487c-ae07-1449583fb126", "identity_type": "organization", "email_address": "contact@northwind-traders.example", "phone_number": "+47 22 12 34 56", "city": "Oslo", "country": "NO", "created_at": "2026-05-23T13:42:16.547035Z", "meta_data": { "seed_batch": "table_screenshot_20260523" } } ``` *** ## `create` Register a new `individual` or `organization` through interactive prompts for type, name, email, and optional metadata. Use this for manual onboarding or smoke tests. The fields mirror what you send to the Core identities API. ```bash theme={"system"} blnk identities create [options] ``` **Arguments** The CLI prompts for these values. They are not passed on the command line. | Argument | Type | Required | Description | | :-------------- | :----- | :------- | :------------------------------------------------------------------------------------ | | `identity_type` | string | Yes | Identity type (for example `individual` or `organization`). Prompt: `identity type >` | | `first_name` | string | Yes | First name. Prompt: `first name >` | | `last_name` | string | Yes | Last name. Prompt: `last name >` | | `email_address` | string | Yes | Email address. Prompt: `email >` | | `meta_data` | object | No | Optional metadata as JSON. Prompt: `metadata (json) >`. Press **Enter** to skip. | **Options** | Option | Type | Description | | :----------- | :------ | :-------------------------------------------------------------- | | `--json` | boolean | After a successful create, print the full API response as JSON. | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command: ```bash wrap theme={"system"} blnk identities create ``` The CLI prompts for each argument: ```text wrap theme={"system"} identity type > first name > last name > email > metadata (json) > ``` Press **Enter** at the metadata prompt to leave it empty. ```text 200 Success icon="circle-check" theme={"system"} Identity created: identity_id: idt_3b63c8da-af29-4cc3-ad38-df17d87456e6 identity_type: individual first_name: Jane last_name: Doe email_address: jane@example.com created_at: 2026-05-23T17:07:49Z ``` ```text JSON icon="circle-check" theme={"system"} { "identity_id": "idt_3b63c8da-af29-4cc3-ad38-df17d87456e6", "identity_type": "individual", "first_name": "Jane", "last_name": "Doe", "email_address": "jane@example.com", "created_at": "2026-05-23T17:07:49.876192Z", "meta_data": {} } ``` *** # Install the Blnk CLI Source: https://docs.blnkfinance.com/cli/install Learn how to install your CLI on your machine The Blnk CLI is the official command-line tool to interact with your Blnk Core. Use it to [connect to an instance](/cli/using-blnk-core) and manage ledgers, balances, transactions, identities, and reconciliations from your terminal. Browse commands in the sidebar under [**Resource commands**](/cli/ledgers) and [**Additional commands**](/cli/help). *** ## Install and update Latest version: v1.8.2 To use the Blnk CLI, you need [Node.js](https://nodejs.org/) and [npm](https://www.npmjs.com/package/blnk-cli) on your machine. Install and update the Blnk CLI globally: ```bash bash wrap theme={"system"} npm i -g @blnkfinance/blnk-cli ``` Confirm your installation: ```bash bash wrap theme={"system"} blnk --version ``` ```text 200 Success icon="circle-check" theme={"system"} Blnk CLI: 1.8.2 ``` *** # Ledgers Source: https://docs.blnkfinance.com/cli/ledgers Use the Blnk CLI to list and create ledgers on your Core instance. Use the `ledgers` command to browse ledgers, open one record by ID, and create new ledgers on your connected Core instance. Ledgers group balances and transactions; start here when you need a `ledger_id` for balance setup. | Command | Alias | Description | | :------ | :---- | :----------------------------------------------------- | | list | l | Browse all ledgers in a table; add `--id` to fetch one | | create | c | Create a ledger via interactive prompts | *** ## `list` See every ledger on your connected Core instance in a paginated table with ID, name, and created time. Use this when you need a `ledger_id` before creating balances or when auditing how your books are partitioned. ```bash theme={"system"} blnk ledgers list [options] ``` **Options** | Option | Type | Default | Description | | :----------- | :------ | :------ | :------------------------------------------------------------------------------ | | `--{field}` | string | - | Filter by any indexed field. Quote comma-separated values for multiple matches. | | `-p, --page` | integer | `1` | Page number. | | `--per-page` | integer | `10` | Results per page. | | `-h, --help` | boolean | - | Show help for this command. | **Usage** Run the command to view a paginated table of ledgers: ```bash wrap theme={"system"} blnk ledgers list ``` Paginate with `-p` and `--per-page`: ```bash wrap theme={"system"} blnk ledgers list --page 2 --per-page 3 ``` ```text 200 Success icon="circle-check" theme={"system"} ┌---------------- ---------------- ----------------------------┐ │ ID │ Name │ Created At │ | --------------- | ---------------- | --------------------------- | │ ldg_46074f0e... │ EUR Operations │ 2026-05-23T17:07:49.876192Z │ │ ldg_0b89c4c5... │ Card Programs │ 2026-05-23T17:07:49.709619Z │ │ ldg_6a55c36f... │ Savings Accounts │ 2026-05-23T17:07:44.97366Z │ └---------------- ---------------- ----------------------------┘ 3 results found Page 1/1 ``` *** ## `list --id` Resolve one ledger by `ledger_id`, including its name and `meta_data`. Pass `--json` to print the same payload your application receives from the Core API. ```bash theme={"system"} blnk ledgers list --id [options] ``` **Options** | Option | Type | Required | Description | | :----------- | :------ | :------- | :--------------------------------------------------------------------------- | | `--id` | string | Yes | Ledger ID to fetch (for example `ldg_46074f0e-898d-4b48-bced-d647816168c0`). | | `--json` | boolean | No | Print the full API object as JSON instead of a formatted summary. | | `-h, --help` | boolean | No | Show help for this command. | **Usage** Run the command with a ledger ID: ```bash wrap theme={"system"} blnk ledgers list --id ldg_46074f0e-898d-4b48-bced-d647816168c0 ``` ```text 200 Success icon="circle-check" theme={"system"} Ledger details: name: EUR Operations ledger_id: ldg_46074f0e-898d-4b48-bced-d647816168c0 created_at: 2026-05-23T17:07:49Z ``` ```text JSON icon="circle-check" theme={"system"} { "ledger_id": "ldg_46074f0e-898d-4b48-bced-d647816168c0", "name": "EUR Operations", "created_at": "2026-05-23T17:07:49.876192Z", "meta_data": { "currency_focus": "EUR", "region": "EU" } } ``` *** ## `create` Add a ledger through guided prompts for name and optional JSON metadata. Create ledgers first when you are standing up a new product, region, or currency scope. Balances and transactions hang off a ledger. ```bash theme={"system"} blnk ledgers create [options] ``` **Arguments** The CLI prompts for these values. They are not passed on the command line. | Argument | Type | Required | Description | | :---------- | :----- | :------- | :------------------------------------------------------------------------------- | | `name` | string | Yes | Ledger name. Prompt: `ledger name >` | | `meta_data` | object | No | Optional metadata as JSON. Prompt: `metadata (json) >`. Press **Enter** to skip. | **Options** | Option | Type | Description | | :----------- | :------ | :-------------------------------------------------------------- | | `--json` | boolean | After a successful create, print the full API response as JSON. | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command: ```bash wrap theme={"system"} blnk ledgers create ``` The CLI prompts for each argument: ```text wrap theme={"system"} ledger name > metadata (json) > ``` Press **Enter** at the metadata prompt to leave it empty. ```text 200 Success icon="circle-check" theme={"system"} Ledger created: name: My Ledger ledger_id: ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc created_at: 2026-05-23T17:07:49Z ``` ```text JSON icon="circle-check" theme={"system"} { "ledger_id": "ldg_073f7ffe-9dfd-42ce-aa50-d1dca1788adc", "name": "My Ledger", "created_at": "2026-05-23T17:07:49.876192Z", "meta_data": {} } ``` *** # Output formats Source: https://docs.blnkfinance.com/cli/output-formats How the Blnk CLI outputs results in table, summary, and JSON formats. The Blnk CLI does not use a global `--format` flag. Output depends on the command you run and the flags you pass. *** ## Output modes The CLI picks a format from the command you run; there is no global `--format` switch. Use this table to know what to expect before you pipe output elsewhere. | Mode | When you see it | Typical commands | | :---------- | :--------------------------------------------------- | :--------------------------------------------------- | | **Table** | Paginated list with column headers and a footer | `blnk ledgers list`, `blnk balances list`, … | | **Summary** | Labeled fields after create or single-resource fetch | `blnk ledgers create`, `blnk ledgers list --id ` | | **JSON** | Pretty-printed API object | Any command with `--json` where supported | *** ## Table output Resource `list` commands (without `--id`) render an ASCII table with column headers, then a footer with total results and page number. Tables are meant for scanning many records in the terminal, not for machine parsing. Run any resource `list` command without `--id`: ```bash wrap theme={"system"} blnk ledgers list ``` ```text 200 Success icon="circle-check" theme={"system"} ┌---------------- ---------------- ----------------------------┐ │ ID │ Name │ Created At │ | --------------- | ---------------- | --------------------------- | │ ldg_46074f0e... │ EUR Operations │ 2026-05-23T17:07:49.876192Z │ │ ldg_0b89c4c5... │ Card Programs │ 2026-05-23T17:07:49.709619Z │ │ ldg_6a55c36f... │ Savings Accounts │ 2026-05-23T17:07:44.97366Z │ └---------------- ---------------- ----------------------------┘ 7 results found Page 1/3 ``` *** ## Summary output After `create` or when you run `list --id` without `--json`, the CLI prints a short labeled block with the fields most people need day to day. This is the default human-readable detail view; switch to JSON when you need every API field. ```text 200 Success icon="circle-check" theme={"system"} Ledger details: name: EUR Operations ledger_id: ldg_46074f0e-898d-4b48-bced-d647816168c0 created_at: 2026-05-23T17:07:49Z ``` *** ## JSON output Pass `--json` on supported commands to print the raw Core API response as pretty-printed JSON. Ideal for scripts, `jq`, or comparing CLI behavior to your HTTP integration. ```bash wrap theme={"system"} blnk ledgers list --id ldg_46074f0e-898d-4b48-bced-d647816168c0 --json ``` ```text 200 Success icon="circle-check" theme={"system"} { "ledger_id": "ldg_46074f0e-898d-4b48-bced-d647816168c0", "name": "EUR Operations", "created_at": "2026-05-23T17:07:49.876192Z", "meta_data": { "currency_focus": "EUR", "region": "EU" } } ``` *** # Reconciliation Source: https://docs.blnkfinance.com/cli/reconciliations Use the Blnk CLI to list reconciliations on your Core instance. Use the `reconciliations` command to list reconciliation runs and their status against upload batches on Core. Use it after imports or scheduled jobs to confirm a batch finished or to find runs still in progress. | Command | Alias | Description | | :------ | :---- | :-------------------------------------- | | list | l | Browse reconciliation jobs with filters | *** ## `list` Review reconciliation jobs and their status in a paginated table, including upload batch IDs. Filter with `--status` or `--upload-id` when you are tracking a specific import or confirming a run completed. ```bash theme={"system"} blnk reconciliations list [options] ``` **Options** | Option | Type | Default | Description | | :----------- | :------ | :------ | :---------------------------------------------------------------------------------------- | | `--{field}` | string | - | Filter by any indexed field (for example `--status COMPLETED`, `--upload-id upl_abc123`). | | `-p, --page` | integer | `1` | Page number. | | `--per-page` | integer | `10` | Results per page. | | `-h, --help` | boolean | - | Show help for this command. | **Usage** Run the command to view reconciliations on your Core instance: ```bash wrap theme={"system"} blnk reconciliations list ``` Filter and paginate: ```bash wrap theme={"system"} blnk reconciliations list --page 2 --per-page 20 blnk reconciliations list --status COMPLETED --upload-id upl_abc123 ``` ```text 200 Success icon="circle-check" theme={"system"} ┌──────────────── ──────────────── ────────── ─────── ───────── ─────────────────────────── ───────────────────────────┐ │ Reconciliation ID │ Upload ID │ Status │ Matched │ Unmatched │ Started At │ Completed At │ | ---------------- | ---------------- | ---------- | ------- | --------- | --------------------------- | --------------------------- | │ rec_a8f2c91d... │ upl_7b3e4f12... │ COMPLETED │ 142 │ 3 │ 2026-05-23T17:05:12.441201Z │ 2026-05-23T17:05:18.902331Z │ │ rec_2d19e0a4... │ upl_7b3e4f12... │ COMPLETED │ 98 │ 0 │ 2026-05-22T09:14:01.120000Z │ 2026-05-22T09:14:06.558442Z │ │ rec_f31b8c67... │ upl_c4a91e55... │ RUNNING │ 45 │ 12 │ 2026-06-04T14:22:33.000000Z │ N/A │ └──────────────── ──────────────── ────────── ─────── ───────── ─────────────────────────── ───────────────────────────┘ 3 results found Page 1/1 ``` ```text No results icon="circle-x" theme={"system"} No results found. ``` *** # Typesense reindex Source: https://docs.blnkfinance.com/cli/reindex Start and monitor Typesense search reindex jobs from the CLI. Use the `reindex` command to start a Typesense search reindex on your connected Core instance and check its progress. Requires CLI **1.8.1** or later. | Command | Alias | Description | | :------ | :---- | :--------------------------- | | create | c | Start a new reindex job | | list | l | Check current reindex status | *** ## `create` Trigger a full reindex that reads records from your database and indexes them into Typesense collections. Run this after deploying a new Typesense instance or when search results are missing data that still exists in Core. ```bash theme={"system"} blnk reindex create [options] ``` **Options** | Option | Type | Description | | :----------- | :------ | :-------------------------------------------------------------- | | `--json` | boolean | After a successful create, print the full API response as JSON. | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command on your connected Core instance: ```bash wrap theme={"system"} blnk reindex create ``` ```text 200 Success icon="circle-check" theme={"system"} Reindex started: status: pending total_records: 0 processed_records: 0 ``` ```text JSON icon="circle-check" theme={"system"} { "message": "Reindex operation started", "progress": { "status": "pending", "phase": "", "total_records": 0, "processed_records": 0, "started_at": "0001-01-01T00:00:00Z" } } ``` *** ## `list` Check the current reindex job, including status, phase, record counts, and timestamps. Poll with `list` while a job is running; when `status` is `completed`, verify search coverage in Typesense if needed. ```bash theme={"system"} blnk reindex list [options] ``` **Options** | Option | Type | Description | | :----------- | :------ | :------------------------------------------------------------------ | | `--json` | boolean | Print the full API response as JSON instead of a formatted summary. | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command: ```bash wrap theme={"system"} blnk reindex list ``` ```text 200 Success icon="circle-check" theme={"system"} Reindex status: status: completed phase: done total_records: 181 processed_records: 181 started_at: 2026-06-04T19:42:21Z completed_at: 2026-06-04T19:42:22Z ``` ```text JSON icon="circle-check" theme={"system"} { "status": "completed", "phase": "done", "total_records": 181, "processed_records": 181, "started_at": "2026-06-04T19:42:21.267309645Z", "completed_at": "2026-06-04T19:42:22.644924172Z" } ``` ```text wrap No job icon="circle-x" theme={"system"} No results found. ``` *** # Transactions Source: https://docs.blnkfinance.com/cli/transactions Use the Blnk CLI to list, create, and update transactions on your Core instance. Use the `transactions` command to list transfers, inspect a single post, create transactions interactively, and commit, void, or refund inflight work. Reach for this command when you are debugging money movement or testing a flow on Core. | Command | Alias | Description | | :------ | :---- | :------------------------------------------------------------ | | list | l | Browse transfers with filters; add `--id` for one transaction | | create | c | Post a transfer via interactive prompts | | update | u | Commit, void, or refund an existing transaction | *** ## `list` Monitor money movement in a paginated table with amount, currency, status, and related IDs. Filter by `--status`, `--currency`, or `--balance-id` to audit queued work, applied posts, or activity on one account. ```bash theme={"system"} blnk transactions list [options] ``` **Options** | Option | Type | Default | Description | | :----------- | :------ | :------ | :------------------------------------------------------------------------------------ | | `--{field}` | string | - | Filter by any indexed field (for example `--status APPLIED`, `--currency "USD,EUR"`). | | `-p, --page` | integer | `1` | Page number. | | `--per-page` | integer | `10` | Results per page. | | `-h, --help` | boolean | - | Show help for this command. | **Usage** Run the command to view a paginated table of transactions: ```bash wrap theme={"system"} blnk transactions list ``` Filter and paginate: ```bash wrap theme={"system"} blnk transactions list --page 3 --per-page 25 blnk transactions list --status APPLIED --currency "USD,EUR" blnk transactions list --balance-id bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f --status APPLIED ``` ```text 200 Success icon="circle-check" theme={"system"} ┌──────────────── ───────── ──────── ──────── ──────── ──────────────── ──────────────── ──────────────── ───────────────────────────┐ │ Transaction ID │ Parent ID │ Amount │ Currency │ Status │ Source │ Destination │ Reference │ Created At │ | ---------------- | --------- | -------- | -------- | -------- | ---------------- | ---------------- | ---------------- | --------------------------- | │ txn_fa19841f... │ N/A │ 12.4 │ USDC │ APPLIED │ bln_c4984d90... │ bln_d98d6524... │ ref_4809d836... │ 2026-06-03T12:35:29.822965Z │ │ txn_ac1d86a2... │ N/A │ 12.4 │ USDC │ INFLIGHT │ bln_c4984d90... │ bln_d98d6524... │ ref_94175e3e... │ 2026-06-03T12:34:52.925262Z │ │ txn_ff016b8b... │ N/A │ 12.4 │ USDC │ QUEUED │ bln_c4984d90... │ bln_d98d6524... │ ref_94175e3e... │ 2026-06-03T12:34:52.925262Z │ └──────────────── ───────── ──────── ──────── ──────── ──────────────── ──────────────── ──────────────── ───────────────────────────┘ 98 results found Page 1/33 ``` Status values may appear colorized in your terminal. *** ## `list --id` Inspect one transaction end to end: source, destination, reference, status, and metadata. Pass `--json` when you need precision, inflight flags, or parent/child links exactly as Core returns them. ```bash theme={"system"} blnk transactions list --id [options] ``` **Options** | Option | Type | Required | Description | | :----------- | :------ | :------- | :-------------------------------------------------------------------------------- | | `--id` | string | Yes | Transaction ID to fetch (for example `txn_fa19841f-faa5-4838-9295-b7278a4ff71d`). | | `--json` | boolean | No | Print the full API object as JSON instead of a formatted summary. | | `-h, --help` | boolean | No | Show help for this command. | **Usage** Run the command with a transaction ID: ```bash wrap theme={"system"} blnk transactions list --id txn_fa19841f-faa5-4838-9295-b7278a4ff71d ``` ```text 200 Success icon="circle-check" theme={"system"} Transaction details: amount: 12.4 USDC transaction_id: txn_fa19841f-faa5-4838-9295-b7278a4ff71d source: bln_c4984d90-9414-4450-9d94-120f7b2f6b29 destination: bln_d98d6524-29d8-472e-99cd-8a89e9e01243 reference: ref_4809d836-0e6a-4975-971a-fde118672faa status: APPLIED created_at: 2026-06-03T12:35:29Z meta_data: {"QUEUED_PARENT_TRANSACTION":"txn_ff016b8b-a5c5-4394-90c6-774be65b74bc","allow_overdraft":true} ``` ```text JSON icon="circle-check" theme={"system"} { "transaction_id": "txn_fa19841f-faa5-4838-9295-b7278a4ff71d", "amount": 12.4, "currency": "USDC", "precision": 10000, "source": "bln_c4984d90-9414-4450-9d94-120f7b2f6b29", "destination": "bln_d98d6524-29d8-472e-99cd-8a89e9e01243", "reference": "ref_4809d836-0e6a-4975-971a-fde118672faa", "description": "Test", "status": "APPLIED", "inflight": true, "created_at": "2026-06-03T12:35:29.822965Z", "meta_data": {} } ``` *** ## `create` Post a transfer between balances (or indicators) through prompts for amount, precision, parties, reference, and inflight options. Handy for quick manual tests; multi-leg or high-volume flows should go through your application integration instead. ```bash theme={"system"} blnk transactions create [options] ``` The interactive flow covers common fields. For complex multi-leg or bulk transactions, use your application integration against Core directly. **Arguments** The CLI prompts for these values. They are not passed on the command line. | Argument | Type | Required | Description | | :---------------- | :------ | :------- | :------------------------------------------------------------------------------- | | `amount` | string | Yes | Transaction amount. Prompt: `amount >` | | `precision` | integer | Yes | Amount precision (for example `100` for cents). Prompt: `precision >` | | `currency` | string | Yes | Currency code. Prompt: `currency >` | | `source` | string | Yes | Source balance ID or indicator. Prompt: `source >` | | `destination` | string | Yes | Destination balance ID or indicator. Prompt: `destination >` | | `reference` | string | Yes | Unique reference for the transaction. Prompt: `reference >` | | `description` | string | Yes | Short description. Prompt: `description >` | | `allow_overdraft` | boolean | No | Whether overdraft is allowed. Prompt: `allow overdraft >` | | `inflight` | boolean | No | Whether the transaction is inflight. Prompt: `inflight >` | | `meta_data` | object | No | Optional metadata as JSON. Prompt: `metadata (json) >`. Press **Enter** to skip. | **Options** | Option | Type | Description | | :----------- | :------ | :-------------------------------------------------------------- | | `--json` | boolean | After a successful create, print the full API response as JSON. | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command: ```bash wrap theme={"system"} blnk transactions create ``` The CLI prompts for each argument: ```text wrap theme={"system"} amount > precision > currency > source > destination > reference > description > allow overdraft > inflight > metadata (json) > ``` Press **Enter** at the metadata prompt to leave it empty. ```text 200 Success icon="circle-check" theme={"system"} Transaction created: amount: 100 USD transaction_id: txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d source: bln_source destination: bln_destination reference: ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94 status: APPLIED created_at: 2026-05-23T17:07:49Z meta_data: {} ``` ```text JSON icon="circle-check" theme={"system"} { "transaction_id": "txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d", "amount": 100, "currency": "USD", "source": "bln_source", "destination": "bln_destination", "reference": "ref_f482a1b3-6c2d-4e89-a17b-3d5e8f2a1c94", "status": "APPLIED", "created_at": "2026-05-23T17:07:49.876192Z", "meta_data": {} } ``` *** ## `update` Move an existing transaction through its lifecycle: commit inflight funds, void before settlement, or refund after apply. Supply the transaction ID and exactly one of `--commit`, `--void`, or `--refund`. ```bash theme={"system"} blnk transactions update [options] ``` **Arguments** | Argument | Type | Required | Description | | :--------------- | :----- | :------- | :--------------------------------------------------------------------------------- | | `transaction-id` | string | Yes | Transaction ID to update (for example `txn_6164573b-6cc8-45a4-ad2e-7b4ba6a60f7d`). | **Options** | Option | Type | Description | | :----------- | :------ | :------------------------------------ | | `--commit` | boolean | Fully commit an inflight transaction. | | `--void` | boolean | Void an inflight transaction. | | `--refund` | boolean | Refund a transaction. | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command with `--commit` and an inflight transaction ID: ```bash wrap theme={"system"} blnk transactions update --commit txn_ac1d86a2-9332-4676-89fb-630ed967e610 ``` ```text 200 Success icon="circle-check" theme={"system"} Transaction committed: amount: 12.4 USDC transaction_id: txn_fa19841f-faa5-4838-9295-b7278a4ff71d source: bln_c4984d90-9414-4450-9d94-120f7b2f6b29 destination: bln_d98d6524-29d8-472e-99cd-8a89e9e01243 reference: ref_4809d836-0e6a-4975-971a-fde118672faa status: APPLIED created_at: 2026-06-03T12:35:29Z meta_data: {"QUEUED_PARENT_TRANSACTION":"txn_ff016b8b-a5c5-4394-90c6-774be65b74bc","allow_overdraft":true} ``` Run the command with `--void` and an inflight transaction ID: ```bash wrap theme={"system"} blnk transactions update --void txn_ff016b8b-a5c5-4394-90c6-774be65b74bc ``` ```text 200 Success icon="circle-check" theme={"system"} Transaction voided: amount: 12.4 USDC transaction_id: txn_a1b2c3d4-e5f6-7890-abcd-ef1234567890 source: bln_c4984d90-9414-4450-9d94-120f7b2f6b29 destination: bln_d98d6524-29d8-472e-99cd-8a89e9e01243 reference: ref_94175e3e-89ce-41a3-bcdc-83504836637a_q status: VOID created_at: 2026-06-03T12:36:01Z meta_data: {"QUEUED_PARENT_TRANSACTION":"txn_ff016b8b-a5c5-4394-90c6-774be65b74bc","allow_overdraft":true} ``` Run the command with `--refund` and an applied transaction ID: ```bash wrap theme={"system"} blnk transactions update --refund txn_fa19841f-faa5-4838-9295-b7278a4ff71d ``` ```text 200 Success icon="circle-check" theme={"system"} Transaction refunded: amount: 12.4 USDC transaction_id: txn_1877186d-6841-4b9e-88f5-3170de40f656 source: bln_d98d6524-29d8-472e-99cd-8a89e9e01243 destination: bln_c4984d90-9414-4450-9d94-120f7b2f6b29 reference: ref_c6f94d08-ac0e-4923-8487-26f8202a708b status: QUEUED created_at: 2026-06-04T19:27:16Z meta_data: {"QUEUED_PARENT_TRANSACTION":"txn_1877186d-6841-4b9e-88f5-3170de40f656","allow_overdraft":true} ``` *** # Using Blnk Core Source: https://docs.blnkfinance.com/cli/using-blnk-core Connect the Blnk CLI to your Core instance and verify the connection. Use connection commands to create or confirm your connection with your Blnk Core instance before you run resource commands. *** ## `blnk connect` Point the CLI at a Core instance, store your API key locally, and confirm the instance responds before you run resource commands. Run this after install or whenever you switch environments (sandbox, staging, production). ```bash theme={"system"} blnk connect [url] ``` **Arguments** | Argument | Type | Required | Description | | :------- | :----- | :------- | :------------------------------------------------------------------------------------- | | `url` | string | No | The URL of the Blnk Core instance to connect to. Include the protocol (http or https). | **Options** | Option | Type | Description | | :----------- | :------ | :-------------------------- | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command with or without the URL argument. If the URL is not provided, the CLI prompts for it. If you did not pass a URL argument, the CLI prompts for the instance URL, then the API key: ```text wrap theme={"system"} url > api key > ``` Press **Enter** at the API key prompt to skip if your instance does not require a key. While the CLI checks connectivity, you see a spinner such as `Testing connection health`. Once done, the CLI prints the success or error message. ```text 200 Success icon="circle-check" theme={"system"} success: Successfully connected to "https://api.example.com" ``` ```text wrap Invalid API key icon="circle-x" theme={"system"} error: Invalid API key. Verify that the provided API key is correct and try again. ``` ```text wrap Instance unreachable icon="circle-x" theme={"system"} error: Can't connect to instance. Verify the URL and ensure your instance is running. ``` If the API key is wrong, verify the key and run `connect` again. If the instance is unreachable, confirm it is running and reachable from your machine, then try again. *** ## `blnk info` Confirm which Core URL the CLI is using and whether the last health check succeeded, without re-entering credentials. Use it when a command fails with auth or connectivity errors and you need to verify you are on the right instance. ```bash theme={"system"} blnk info ``` **Options** | Option | Type | Description | | :----------- | :------ | :-------------------------- | | `-h, --help` | boolean | Show help for this command. | **Usage** Run the command to see which instance the CLI is configured to use: ```bash wrap theme={"system"} blnk info ``` The CLI prints the current server connection and whether it is connected. ```text 200 Success icon="circle-check" theme={"system"} Current server connection: https://api.example.com [+] connected ``` ```text wrap Not connected icon="circle-x" theme={"system"} Current server connection: Not set [-] not connected ``` *** # Version Source: https://docs.blnkfinance.com/cli/version Print the installed Blnk CLI version. Print the CLI version you have installed. This is a root-level flag, not a separate subcommand. See [Global flags](/cli/global-flags). *** ## `version` Print the installed CLI version and exit. Useful when comparing release notes, reporting a bug, or confirming an `npm` global upgrade took effect. ```bash theme={"system"} blnk --version ``` **Usage** Run the command from any directory: ```bash wrap theme={"system"} blnk --version ``` ```text 200 Success icon="circle-check" theme={"system"} Blnk CLI: 1.8.2 ``` Your version string matches the package you installed with `npm i -g @blnkfinance/blnk-cli`. *** # Profile Source: https://docs.blnkfinance.com/cloud/account/profile Learn how to set up your profile. Your profile found in [Settings > Profile](https://cloud.blnkfinance.com/cloud/settings/profile) is where you manage your user account in Blnk Cloud. Profile settings page showing user account management interface *** ## Names and emails You can change your first and last names. Other team members can see your name in the workspace when you perform an action or are assigned to an issue. Name and email fields in the profile form You cannot change your email address. To update your email address, please [contact Support](mailto:support@blnkfinance.com). *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Security & Access Source: https://docs.blnkfinance.com/cloud/account/security Learn how to manage your account's security and access. The Security & Access settings allow you to manage your passwords, session and advanced security options. You can find it in [Settings > Security & Access](https://cloud.blnkfinance.com/cloud/settings/security). Security & Access settings page overview *** ## Sessions Sessions keep a timesheet of the last time you signed in from a unique device or browser. This helps track and manage your account access across different locations. You can log out by clicking on the `Log out` option for each session. This gives you granular control over which devices remain authenticated. Additional security features include: * Active sessions show device type, location, and timestamp. * Login attempts trigger email notifications. *** ## Set password If you signed up with Google, you can decide to also set a password for your account in case you do not have access to your Google account. Set Password form for Google users Click `Set Password` in the password section and follow the instructions to set a password for your account. *** ## Change password To change your password, provide your current password and input your new password. Then, click on `Change Password` to save your new password. Change Password form with current and new password fields *** ## Forgot password To reset a forgotten password, log out if you are not already logged out. On the sign-in page, click `Forgot Password?` and follow the instructions to reset your password. Sign-in page showing the Forgot Password link *** ## Two-factor authentication To add an extra layer of protection to your account, you can enable two-factor authentication. Two-factor authentication setup process with authenticator app instructions To get started, toggle it on, and follow the instructions to set it up using any of our supported authenticator apps (Authy, Google Authenticator, etc.). *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Sign up and manage your ledger with our back-office dashboard. You can invite teammates to collaborate and manage your ledger operations directly from the dashboard. # Alert statuses Source: https://docs.blnkfinance.com/cloud/alerts/alert-statuses Manage custom alert statuses with names and colours Alert statuses let you define custom names and colours for the statuses you use on alerts. You can create statuses like "Pending", "In Review", or "Fraud review" and assign each a colour so they're easy to spot in your Alerts table and details. You can find it in [Settings > Alert statuses](https://cloud.blnkfinance.com/cloud/settings/alert-statuses). Alert statuses page showing list of statuses with name and colour *** ## View alert statuses The Alert statuses page lists all custom statuses for your organization. For each status you can see: * `Name:` The display name (e.g. "Fraud review", "In Review", "Pending"). * **Colour:** A colour swatch and hex code (e.g. #3B82F6, #84CC16). Colours must be one of the allowed values. Use the `Actions` menu (three dots) on a row to edit or delete that status. *** ## Add a status 1. Click `+ Add status` in the top-right corner. 2. Enter a `Name` for the status (e.g. "Pending Review", "In Progress"). 3. Choose a `Colour` from the allowed options. 4. Click `Add status`. Add alert status form with name and colour fields The new status will appear in the list and can be used when updating alerts. *** ## Edit a status 1. On the Alert statuses page, open the `Actions` menu (three dots) for the status you want to change. 2. Select `Edit`. 3. Update the **Name** and/or **Colour**. 4. Click `Save`. *** ## Delete a status 1. On the Alert statuses page, open the `Actions` menu (three dots) for the status. 2. Select `Delete`. 3. Confirm the deletion. If any alerts still use this status, you must reassign them to another status before you can delete it. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Alerts Source: https://docs.blnkfinance.com/cloud/alerts/overview Learn how to create and manage alerts in Blnk. Alerts let you track and get notified about important events and conditions in your Blnk Core. You can create alerts manually on any record from the dashboard, or trigger them automatically via [the Alerts API](/cloud/reference/create-alert). *** ## Manual flagging You can manually flag records from their details page using `Actions` > `Flag balance`. For balance-specific steps (including status selection and custom status creation), see [Flag balance](/cloud/balances/flag-balance). After you flag a balance, use `Alerts` to update the alert status as needed (for example, `Flagged`, `Investigating`, or `Resolved`). *** ## Working with alerts All alerts in your workspace are displayed in the `Alerts` table, where you can view, filter, and manage them. ### Alerts table The **Alerts** table shows a list of all alerts in your workspace. You can filter alerts by: * `All:` View all alerts in your workspace. * `Assigned to you:` View only alerts assigned to you. You can also filter further by status: * `All:` View all alerts regardless of status. * **Investigating:** Alerts currently under investigation. * `Flagged:` Alerts that have been flagged for review. * **Resolved:** Alerts that have been resolved. Alerts table showing all alert information For each alert, you can see: * **Title:** The title describing the nature of the alert (e.g., "Transaction flagged as suspicious," "Identity flagged as suspicious"). * **Status:** The current status of the alert - flagged, investigating, or resolved. * **Assigned to:** The user responsible for handling the alert. * `Created at:` The date and time the alert was created. ### View alert details Click an alert from the table to open its details panel on the right side of your screen. Alert details summary view The alert details panel shows: * **Name:** The title of the alert (e.g., "Transaction flagged as suspicious"). * **Status:** The current status displayed as a colored tag with an icon - flagged (yellow), investigating (blue), or resolved (green). * **Description:** The description or reason for the alert. * **Assigned to:** The user responsible for handling the alert. * `Date:` The date and time the alert was created. * `Alert ID:` The unique identifier for the alert. Click the copy icon to copy the ID. For a complete view, select `Full info` in the top-right corner of the details panel or press `E` to expand the details. Alert full details page showing flagged records and audit log In the full details page, you can see: * **Flagged records:** A section showing all records (transactions, balances, or identities) affected by the alert. The table displays key information about each record, including currency, amount, status, source, destination, and effective date. * `View record:` Click on any record in the flagged records table to view its full details page. * **Audit log:** A section that tracks all actions and changes made to the alert, allowing you to monitor the alert's history. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Building Your App Logic Source: https://docs.blnkfinance.com/cloud/apps/app-logic Use Cloud APIs to read data, perform actions, and create alerts from your Custom App. 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. | API | When to use it | | -------------------- | --------------------------------------------------------------- | | Data API | Read and filter ledger data through Cloud. | | Proxy API | Perform Core actions through Cloud. | | Other Cloud features | Use Cloud features that are not Core endpoints, such as alerts. | All requests use the Cloud base URL: ```bash theme={"system"} https://api.cloud.blnkfinance.com ``` The installed app API key goes in the `Authorization` header: ```bash theme={"system"} Authorization: Bearer ``` For Proxy and Data API requests, include the selected `instance_id` in the URL: ```bash theme={"system"} ?instance_id= ``` *** ## Quick reference Use the proxy when your app wants to call Blnk Core through Cloud. The format is: ```bash wrap theme={"system"} https://api.cloud.blnkfinance.com/proxy/?instance_id= ``` The `` 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: ```bash List ledgers theme={"system"} GET http://localhost:5001/ledgers ``` ```bash Create transactions theme={"system"} POST http://localhost:5001/transactions ``` With the Cloud Proxy, it becomes: ```bash List ledgers wrap theme={"system"} GET https://api.cloud.blnkfinance.com/proxy/ledgers?instance_id=inst_... ``` ```bash Create transactions wrap theme={"system"} POST https://api.cloud.blnkfinance.com/proxy/transactions?instance_id=inst_... ``` A complete request via the Cloud Proxy would look like: ```bash bash wrap theme={"system"} curl -X GET "https://api.cloud.blnkfinance.com/proxy/ledgers?instance_id=inst_..." \ -H "Authorization: Bearer blnk_..." \ -H "Content-Type: application/json" ``` Proxy endpoints and request shapes. Use the Data API when your app needs to read or filter ledger data through Cloud. The format is: ```bash wrap theme={"system"} https://api.cloud.blnkfinance.com/data/?instance_id= ``` For example, to read transactions: ```bash wrap theme={"system"} curl -X GET "https://api.cloud.blnkfinance.com/data/transactions?instance_id=inst_..." \ -H "Authorization: Bearer blnk_..." \ -H "Content-Type: application/json" ``` You can also add filters to the query string: ```bash wrap theme={"system"} curl -X GET "https://api.cloud.blnkfinance.com/data/transactions?instance_id=inst_...&status_eq=APPLIED¤cy_eq=USD" \ -H "Authorization: Bearer blnk_..." \ -H "Content-Type: application/json" ``` Data API operations and read patterns. Use other Cloud endpoints for features that belong to Cloud itself. Alerts are a good example. You do not call alerts through `/proxy` or `/data`. You call the Alerts API directly. ```bash wrap theme={"system"} https://api.cloud.blnkfinance.com/alerts/flag/ ``` *** ## Example application Let's apply this to build the [Stripe Sync workflow](/cloud/apps/define-workflow#map-your-workflow) we mapped earlier. First, we'll list pay-ins from Stripe and keep only the ones that have actually settled (`status === "succeeded"`): ```typescript fetchStripePayIns.ts wrap theme={"system"} 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. Next, we'll mirror the Stripe pay-in as a transaction in the selected Blnk Cloud instance through the Proxy API: ```typescript sendPayInToBlnk.ts wrap theme={"system"} 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. 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: ```typescript saveSyncRecord.ts wrap theme={"system"} 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. *** Reference Stripe sync implementation. *** Before you ship, review [Best practices](/cloud/apps/best-practices) for securing API keys, portal embedding, and permissions. *** We help you build custom apps for your use case or get help building your own from scratch. # Custom App security and best practices Source: https://docs.blnkfinance.com/cloud/apps/best-practices Security and reliability checklist for Custom Apps before you ship to production in Blnk Cloud. You have wired install, launch, and Cloud API calls. Before users rely on your app in production, use this page as a final checklist for security and reliability. *** ## How Blnk Cloud keeps apps safe Blnk Cloud is designed so your app never needs direct access to the Core. Learn more: [How apps work](/cloud/apps/how-apps-work). | Blnk Cloud handles | You handle in your app | | ----------------------------------------------------------- | ----------------------------------------------------- | | A scoped API key for each install | Protecting and storing the API key safely | | Permission checks on every request | Securing the portal users see in the dashboard | | Cancelling the install if your app does not confirm in time | Checking granted permissions before sensitive actions | *** ## Protect secrets The install callback includes a scoped API key. Treat it like any production credential. * **Encrypt before you save it.** Do not store the key in plain text in your database. Use encryption or a secrets manager that fits your stack. * **Keep keys on the server.** Never put the scoped API key in the browser, in a `portal_url`, or in client-side code. * `Confirm installs quickly.` Return a successful response within about `10 seconds` of the install callback. If your app does not confirm in time, Blnk cancels the installation and revokes the key. * `Log safely.` Use install IDs and key prefixes for debugging. Do not log full API keys, portal session tokens, or third-party secrets. If an install fails after you received the key, assume that key is no longer valid. Wait for a successful install event before using a new key. *** ## Install and uninstall events Blnk sends install and uninstall events to your callback URL. See [Managing installs](/cloud/apps/installation) for payload fields and code examples. * `Process each event once.` Use the `idempotency_key` Blnk sends to detect duplicates. If you already handled an event, return success again without repeating side effects. * `On uninstall, stop using that install.` Mark the installation inactive, remove portal sessions, and stop background jobs that used that install's API key. The install is already tied to the workspace and Cloud instance the user chose in the dashboard. You do not need a separate organization field in the callback payload"save `installed_app_id` and `instance_id` so your backend knows which installation it is serving. *** ## Permissions Request only what your app needs in the manifest. Users can approve a subset of what you ask for. * `At runtime, trust `granted\_permissions`.` That list from the install event is what the user approved. Check it before reads, writes, or alert actions even if your manifest requested more. * `Your app does not inherit user access.` A workspace admin installing your app does not give the app their personal permissions. The app can only do what was granted to that install. Supported permission scopes today: | Scope | What it allows | | -------------- | -------------------------------------------- | | `data:read` | Read ledger data through Cloud. | | `data:write` | Perform write operations through Cloud APIs. | | `alerts:read` | Read alerts for the installed instance. | | `alerts:write` | Create and update alerts through Cloud. | *** ## Calling Blnk Cloud from your backend All Cloud API calls from your app should run on the server using the install's scoped API key. * Use the Cloud base URL and authentication pattern from [App development](/cloud/apps/app-logic). * `Always include the correct `instance\_id\`\` for the installation you are acting on. The key is bound to that instance. * Use the [Proxy API](/cloud/proxy/proxy-api) for Core actions and the [Data API](/cloud/proxy/data-api) for reads and filters. Use alerts endpoints for alert features"not the proxy. Your portal UI should call **your** backend. Your backend calls Blnk. That keeps the scoped key off the user's device. *** ## URLs you register in the manifest When you [register your app](/cloud/apps/register-app), you provide callback and portal URLs that Blnk calls from the cloud. * **Production:** Use public **HTTPS** endpoints that Blnk can reach from the internet. * **Local development:** Use a tunnel service (for example ngrok) and register the HTTPS URL. `localhost` will not work when your app is embedded in the Cloud dashboard. * Blnk validates callback and portal URLs when you register the app. Invalid or unreachable URLs will block registration or launch. *** ## Portal and dashboard embedding When a user launches your app, Blnk opens your `portal_url` inside the dashboard. This section is the full policy for embedding safely. See [Launch your app](/cloud/apps/launch-app) for the launch flow. ### Content Security Policy Set this header on pages Blnk loads in the iframe: ```http Content-Security-Policy wrap theme={"system"} Content-Security-Policy: frame-ancestors 'self' https://blnkfinance.com https://*.blnkfinance.com ``` Only Blnk-owned domains can embed your portal. Other sites"including `localhost` in production"will be blocked by the browser. `'self'` keeps internal redirects (for example from `/portal/enter` to `/portal`) working inside the frame. ### Portal sessions and URLs * Return a `new `portal\_url` every time` someone launches the app from Cloud. * Use **short-lived portal sessions**"roughly 5 to 15 minutes is a good default. Validate the session on every portal page load and API request from the UI. * The `portal_url` is for your interface only. Do not put API keys, provider secrets, or other sensitive data in the link. *** ## Pre-launch checklist Before users rely on your app in production, work through this list: 1. API keys are encrypted at rest 2. No secrets in logs, portal URLs, or client-side code 3. Install callback responds within about 10 seconds 4. Handlers dedupe events with `idempotency_key` 5. Uninstall disables the install and stops related jobs 6. Manifest requests only needed scopes 7. Backend checks `granted_permissions` before sensitive actions 8. Every Cloud API call includes the correct `instance_id` 9. Production URLs are public HTTPS endpoints Blnk can reach 10. Local testing uses a tunnel, not raw `localhost` in the manifest 11. `frame-ancestors` policy is set on portal pages 12. Fresh portal URL and short-lived session on each launch 13. Portal talks to your backend; backend talks to Blnk *** We help you build custom apps for your use case or get help building your own from scratch. # Setting Up Your Codebase Source: https://docs.blnkfinance.com/cloud/apps/codebase-setup Set up the routes, secrets, and app structure you need to build a Custom App. There is no required framework, language, or folder structure for building a Custom App. You can use any stack that works for your team. What matters is that your app has the right pieces for Blnk Cloud to install it, launch it, and let it talk to the selected Cloud instance safely. For a security checklist before production, see [Best practices](/cloud/apps/best-practices). This page explains how to set up your codebase before you start building the full workflow, using the Stripe Sync app as an example: *** ## What your codebase needs Every Custom App is made up of four parts: | Part | What it does | | ------------------ | ---------------------------------------------------------------------------------- | | App routes | Endpoints Blnk Cloud calls during install, uninstall, and launch. | | Persistent storage | Where the app saves install records, encrypted API keys, and portal sessions. | | Backend logic | Server-side code that calls Cloud APIs and any external services the app needs. | | App portal | The user-facing UI that opens inside the Cloud dashboard when the app is launched. | The backend is the only part that holds your secrets. Ideally, the portal UI should never talks to Blnk directly; it should always go through your backend. *** ## Set up app routes Your app needs routes that Cloud can call to install, uninstall, and launch your app. You can name the routes however you want. The important thing is that each route exists and returns a response. | Route type | Example route | What it does | | ------------------------------ | -------------------- | -------------------------------------------------------------- | | Install and uninstall callback | `POST /api/callback` | Receives install and uninstall events from Blnk Cloud. | | Portal generator | `POST /api/portal` | Creates a short-lived portal URL when a user launches the app. | `For our demo Stripe Sync app,` we'll use Express to set up the routes: ```js routes.ts wrap theme={"system"} import express from "express"; import { RelatedTopics } from "/snippets/related-topics.jsx"; const router = express.Router(); router.post("/api/callback", async (req, res) => { // Handle install and uninstall events }); router.post("/api/portal", async (req, res) => { // Create a short-lived portal URL }); ``` *** ## Store app install data When a user installs your app, Blnk Cloud sends installation details to your callback route. Your app needs a persistent place to store that data because it will need it later when the app is launched or when it makes API calls. **Note:** You can use any database you want. For our Stripe Sync example, we'll go with a simple SQLite instance. At minimum, your app should store the following data from the [install payload](/cloud/apps/installation): | Field | Why you need it | | --------------------- | ---------------------------------------------------------------------------- | | `installed_app_id` | Identifies this specific app installation. | | `app_id` | Identifies the app that was installed. | | `instance_id` | Tells you which Cloud instance the app should work with. | | `api_key` | Lets your backend call Cloud APIs for this installation. Store it encrypted. | | `api_key_prefix` | Helps you identify the key without exposing the full secret. | | `granted_permissions` | Tells your app what the user allowed it to do. | | `status` | Tracks whether the install is active or uninstalled. | Do not store install data only in memory. If your server restarts, the app still needs to know which instance it is connected to and which key to use. *** ## Security and best practices Custom Apps receive scoped access to a Cloud instance during installation. Design your app so that access is stored safely, used only on the server, and checked before every action. 1. `Keep the API keys on the server:` The `api_key` from the install payload should only be used by your backend. Do not expose it in browser code, local storage, cookies, portal URLs, client-side responses, or logs. 2. **Let your backend call Cloud:** When the app portal needs data, it should not make requests to Blnk directly. Instead, it should call your backend first, then your backend speaks to Blnk. 3. **Encrypt API keys at rest:** Store the full `api_key` encrypted. You can store `api_key_prefix` in plain text because it only helps identify the key. Do not use the prefix to authenticate requests. 4. `Use short-lived portal sessions:` When Cloud launches your app, return a fresh `portal_url`. Do not return a permanent URL that always opens the app. If a session expires, ask the user to launch the app again from Cloud. 5. **Sign portal sessions:** Use a `SESSION_SECRET` to sign portal sessions. ```env theme={"system"} SESSION_SECRET="replace-with-a-long-random-string" ``` 6. `Check permissions before actions:` Store `granted_permissions` from the install payload. Before your app performs an action, check that the required permission was granted. For example, an app with only `data:read` should not perform write actions. 7. `Validate the install before launch:` Before creating a portal session, confirm that the install exists, is active, and matches the `installed_app_id`, `app_id`, and `instance_id` in the portal request. *** Reference Stripe sync implementation. *** We help you build custom apps for your use case or get help building your own from scratch. # Define your workflow Source: https://docs.blnkfinance.com/cloud/apps/define-workflow Plan what your Custom App should do before you write code - narrow scope, one-sentence purpose, and a clear step-by-step workflow. Before writing code, define the workflow your app should support: The best Custom Apps are focused. They help a team complete one job inside Cloud instead of trying to become a large internal system. A good mental model is to think of each Custom App as a feature of your Blnk Cloud workspace, instead of a complete app. **For our Stripe Sync app,** its job is: ```text Stripe Sync app description wrap theme={"system"} Import pay-ins and payouts from Stripe into Blnk and syncing them with existing customer identities in Blnk. ``` From there, map the work the app needs to handle. Here's the workflow for our Stripe Sync app: | Step | What the app needs to do | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | Sync from Stripe | Connect to a Stripe account and read pay-ins and payouts (and related Stripe ledger data as needed). Also sync the customer identities from Stripe to Blnk. | | Import to Blnk | Create or update ledger transactions in the selected Cloud instance so Blnk mirrors Stripe activity. | This gives the app a clear boundary of what the workflow is responsible for and what should happen at each step. If done well, you can now easily switch payment providers without worrying about the Stripe data being lost. Keep the workflow narrow. If the app starts covering unrelated jobs, you should consider splitting those jobs into separate apps. *** We help you build custom apps for your use case or get help building your own from scratch. # How Custom Apps work Source: https://docs.blnkfinance.com/cloud/apps/how-apps-work A mental model for how Custom Apps fit with Blnk Cloud - components, lifecycle, permissions, security, and auditability. A Custom App is a third-party app that runs inside Blnk Cloud, scoped to a single organization and Cloud instance. It can read ledger data, perform Core actions, talk to external systems, and surface a custom UI inside the Cloud dashboard. Custom Apps snapshot This page explains how a Custom App works with Blnk: the components it has, the lifecycle it goes through, and the security and auditability guarantees you get for free by building on Cloud. Use it as the mental model before you [install](/cloud/apps/install-app) an app or [build your own](/cloud/apps/define-workflow). When you are ready to ship, review [Best practices](/cloud/apps/best-practices). *** ## Cloud as the trust boundary Apps do not hold direct Core credentials; plus a custom app should not call Core directly. Instead, they receive an install-specific key scoped to one organization, one instance, and approved permissions. Cloud mediates every request. Every request flows through Blnk Cloud at `https://api.cloud.blnkfinance.com`, authenticated with the scoped API key issued at install time. Cloud exposes three families of endpoints to your app: | Surface | URL path | Purpose | | ----------------- | ------------- | ---------------------------------------------------------------------- | | Proxy API | `/proxy/...` | Perform Core actions like creating ledgers, balances, or transactions. | | Data API | `/data/...` | Read and filter ledger data through Cloud. | | Cloud-native APIs | `/alerts/...` | Use features that belong to Cloud itself, like alerts. | This gateway pattern is what makes Cloud the trust boundary: your app gets a scoped key for one organization and one instance, and Cloud is responsible for routing the call, enforcing permissions and rate limits, and recording what happened. Request flow showing the app backend calling Cloud proxy, data, and alerts endpoints, which forward to Blnk Core or Cloud features For the full surface, see: Cloud APIs for reads, actions, and alerts. *** ## The Custom App lifecycle A Custom App moves through three stages: registration, installation, and launch. Each stage hands the next one a small set of well-defined values. The Custom App lifecycle You declare your app to Cloud by submitting a manifest with the routes Cloud should call and the permissions your app needs. 1. You upload the manifest in Cloud with display name, callback URL, portal URL, and requested permissions. 2. Cloud lists your app in the Apps library for your organization only. 3. Workspace members can now find and install it on the Cloud instances they use. Add your app to the Apps library with a manifest. Installation is where Cloud mints credentials and your app provisions itself for that specific organization and instance. 1. The user clicks `Install` and reviews the requested permissions. 2. The user grants the permissions and confirms with their password and 2FA. 3. Cloud creates a scoped API key for this installation only. 4. Cloud sends an install event to your callback URL with the install record. 5. Your app stores the install (with the API key encrypted at rest) and returns `2xx`. 6. The install becomes active and the user can launch the app. Handle install and uninstall events for your Custom App. Launching is how the app's UI gets into the dashboard, with a fresh, short-lived session every time. 1. The user clicks `Launch app` in Cloud. 2. If the app requires it, the user confirms with their password and 2FA (same rules as install). 3. Cloud sends a portal request to your portal generator URL. 4. Your app validates the install and creates a short-lived portal session. 5. Your app returns a `portal_url` containing the session token. 6. Cloud embeds that URL inside the dashboard. 7. The user works in the app, while your backend talks to Cloud using the install API key. How a Custom App is launched and embedded inside Blnk Cloud Publish and launch your Custom App in Blnk Cloud. *** ## Application permissions Custom Apps have two layers of permissions that work together: * **What your app can access.** Your manifest declares the scopes the app needs. Cloud enforces these at the API gateway, regardless of what the signed-in user can do. * **What the user approved.** Workspace members choose a subset of the requested scopes during installation. Those granted scopes cannot be changed after install. The supported scopes are: | Scope | What it allows | | -------------- | ----------------------------------------------------------------- | | `data:read` | Allows apps to retrieve ledger data through Cloud. | | `data:write` | Allows apps to perform write operations through Cloud APIs. | | `alerts:read` | Read alerts available to the installed organization and instance. | | `alerts:write` | Create and update alerts through Cloud APIs. | Apps do not bypass Cloud or connect directly to Blnk Core. Even with write permissions, app actions are scoped to the installed organization and instance, routed through Cloud, and recorded in the audit trail. For access to granular permission scopes, please [contact us](mailto:support@blnkfinance.com?subject=Interested%20in%Production%20License) for a production license. At runtime, `granted_permissions` from the install payload is the source of truth. Always check it before performing an action " even if your manifest requested more, the user may have granted less. Apps never inherit a user's permissions. If your manifest does not request a scope, the app cannot use it, even if the user installing it has full access. *** ## Auditability Because Cloud is the gateway for every app action, you get a clear audit trail without building your own: * **Every Core action is mediated.** Calls flow through `/proxy` and `/data`, so Cloud captures who acted, on what instance, with which key. * **Actions are attributable to a specific install.** Each install has its own `installed_app_id` and `api_key_prefix`, so activity can be traced back to a specific app installation, not a generic key. * **Permission grants are recorded at install time.** What the user approved is captured in `granted_permissions` and visible to the workspace. * **Install and uninstall events are idempotent.** Each event carries an `idempotency_key`. Your handler should treat duplicate keys as already processed. * **Uninstall is a clean break.** When a user uninstalls, Cloud sends an uninstall event to your callback URL. If your app returns `2xx`, Cloud marks the install uninstalled and revokes the API key. You can review app activity alongside other workspace activity in the [audit logs](/cloud/organization/audit-logs). *** ## Limitations and constraints A few things to keep in mind as you plan your app: * **Apps are private by default.** Apps you register appear in your organization's Apps library. Installing an app is separate: each Cloud instance has its own install state. * **One install per organization + instance.** Installing an app in one instance does not make it available in another. Each install has its own scoped key. * **Self-hosted Core needs to be reachable.** If your instance is connected and self-hosted, set the Core URL and key, and [whitelist Cloud IPs](/cloud/instances/overview#core-url) so Cloud can reach it. * **Apps cannot exceed requested permissions.** Even if the signed-in user has broader access, the app is bounded by the scopes in its manifest and what the user granted. * **Portal sessions are short-lived by design.** If a session expires, the user re-launches the app from Cloud. *** # Installing Custom Apps Source: https://docs.blnkfinance.com/cloud/apps/install-app Learn how to install and activate custom apps in your Blnk Cloud workspace. This guide shows you how to install an app from the Apps library in Blnk Cloud. When you install an app, it becomes available inside the organization and Cloud instance you selected. This means an app can be installed in one instance and unavailable in another. Before you start, check that you are in the right organization and have selected the instance where you want the app to be available. The Apps library can include two types of apps: * **Official apps:** Built and managed by Blnk. * **Private apps:** Built and managed by your organization. If you are building your own Custom App, see [Custom Apps overview](/cloud/apps/overview). For a runnable Stripe Sync example you can clone and register in Cloud, use the [blnkfinance/apps-demo](https://github.com/blnkfinance/apps-demo) repository on GitHub. *** ## Prerequisites If your instance is deployed on Cloud, you're set to go. For connected self-hosted instances: * Make sure you set your `Core URL` and `Core secret key` in your [instance details](/cloud/instances/overview#edit-instances). * Make sure your Core URL is reachable. If your Core is set up in a private network, [whitelist the Cloud IPs](/cloud/instances/overview#core-url) to ensure secure access to your Core via Cloud. *** ## Install a Custom App To install a Custom App: Navigate to the bottom left section of your side navigation and open the `Apps` library. **Note:** Make sure you're in the right instance where you want the app to be available. If you're not sure, you can confirm by checking the active instance in the top right corner of your screen. Browse the library for the app you want, and click to open its details. Review what it does, the permissions it requires, and any other details you need. Confirm that this is the app you need, then click `Install`. Next, review what the app is allowed to access or do inside your ledger. Screenshot of user confirming permissions before completing install. This helps protect your data and ensures the app only does what you want it to. 1. Review the requested permissions. 2. Select the permissions you want to grant. 3. Click `Continue` if you're happy with the permissions. Finally, confirm the installation by entering your account password and 2FA code (if enabled). This final step helps protect the workspace from unauthorized app installs. Now you can launch the app from the app details page by clicking `Launch app` from the top right corner of the app details page. Launching is usually one click. Some apps prompt for your password and 2FA again before opening. *** ## Uninstall a Custom App To uninstall a Custom App: From the `Apps` library, open the app you want to uninstall. Make sure you are viewing the same organization and instance where the app is installed. Click `Uninstall` from the app details page. If uninstall fails, wait a few minutes and try again. *** ## Troubleshooting If something goes wrong during installation, here are a few things you can try: | Issue | What to do | | -------------------------------- | -------------------------------------------------------------------------------------------- | | Permissions do not load | Reopen the install flow and try again. | | Password or 2FA is incorrect | Check your password or 2FA code and try again. You may see `Authentication failed`. | | Install fails | Return to the app details page and click `Install` again. | | App is not visible after install | Confirm that you are viewing the same organization and instance where the app was installed. | | Private app not loading | Check that your app is live and accessible via Cloud. | *** # Set Up Install and Uninstall Events Source: https://docs.blnkfinance.com/cloud/apps/installation Handle install and uninstall events for your Custom App. A user finds your app in the Apps library, reviews the permissions it requests, and confirms the install. After that, Blnk Cloud sends an event to your app so you can provision the installation on your side. Here's what happens during installation: 1. The user clicks `Install` from the app details page. 2. The user reviews and approves the permissions. 3. Blnk Cloud creates a scoped API key for the installation. 4. Blnk Cloud sends the install event to your backend via the [callback URL](/cloud/apps/codebase-setup#set-up-app-routes). 5. Your app stores the install details and returns a `2xx` response. 6. If the response succeeds, the app becomes active in Cloud. See [Best practices](/cloud/apps/best-practices) for how to protect API keys, handle duplicate events safely, and prepare for production. *** ## Handling the install event An install event tells your app that a user has connected it to a specific workspace and Cloud instance. The install is already scoped to that context in Blnk"you do not receive a separate organization field in the callback. Blnk Cloud sends a payload like this: ```json install_payload.json {8} theme={"system"} { "installed_app_id": "instapp_...", "app_id": "app_...", "instance_id": "inst_...", "api_key": "blnk_...", "api_key_prefix": "blnk_abc", "granted_permissions": ["data:read", "data:write"], "idempotency_key": "install:instapp_..." } ``` | Field | Description | | --------------------- | ------------------------------------------------------------------------------------- | | `installed_app_id` | The ID for this specific app installation. | | `app_id` | The ID of the app that was installed. | | `instance_id` | The Cloud instance where the app was installed. | | `api_key` | The scoped API key your backend uses to call Cloud APIs for this install. | | `api_key_prefix` | A safe prefix for identifying the key without exposing the full secret. | | `granted_permissions` | The permissions the user approved during installation. | | `idempotency_key` | A unique key for this install event. Use it to avoid processing the same event twice. | Once you receive the install event, you need to save the install details so your backend can use them later. `Note:` If your app does not return a `2xx` response within 10 seconds, Blnk cancels the installation, revokes the API key, and the install fails. For our Stripe Sync app, our install handler looks like this: ```js routes.ts wrap theme={"system"} app.post("/api/callback", async (req, res) => { const event = req.body; if (event.idempotency_key?.startsWith("install:")) { // Never persist the raw `api_key` in your database " encrypt it at rest (see Codebase setup). await installs.save({ installed_app_id: event.installed_app_id, app_id: event.app_id, instance_id: event.instance_id, api_key_encrypted: encryptSecret(event.api_key), api_key_prefix: event.api_key_prefix, granted_permissions: event.granted_permissions, status: "active", idempotency_key: event.idempotency_key }); return res.status(200).json({ ok: true }); } }); ``` Use whatever encryption or key-management approach fits your stack before you write the secret to your database. *** ## Handling the uninstall event When a user uninstalls your app, Blnk Cloud sends an uninstall event to the same [callback URL](/cloud/apps/codebase-setup#set-up-app-routes). If your app returns `2xx`, Cloud marks the install uninstalled and revokes the app's API key. If the callback fails, the uninstall does not complete and the API key remains valid. Your app should use this event to stop treating the installation as active. You can also clean up any resources you created for that organization or instance. ```json uninstall_payload.json {6} theme={"system"} { "installed_app_id": "instapp_...", "app_id": "app_...", "instance_id": "inst_...", "uninstalled_at": "2026-04-21T12:00:00Z", "idempotency_key": "uninstall:instapp_..." } ``` For the Stripe Sync app, uninstalling should mark the installation as inactive so the backend stops using the stored install record. ```js routes.ts wrap theme={"system"} app.post("/api/callback", async (req, res) => { const event = req.body; if (event.idempotency_key?.startsWith("uninstall:")) { await installs.update({ installed_app_id: event.installed_app_id, status: "inactive" }); return res.status(200).json({ ok: true }); } }); ``` You can also choose to delete encrypted keys, remove portal sessions, or clean up instance-specific resources. *** ## Handling duplicate events Cloud sends one install or uninstall request per event, with a 10 second timeout. Your handler should still be safe to run more than once. Use the `idempotency_key` to check whether you have already processed the event. If you have already processed the event, return a `200` response to avoid processing the event again: ```js routes.ts wrap theme={"system"} app.post("/api/callback", async (req, res) => { const event = req.body; const existingEvent = await checkIfEventProcessed(event.idempotency_key); if (existingEvent) { return res.status(200).json({ ok: true }); } // Process the event }); ``` *** ## Test the installation flow Before moving to app development, test the install and uninstall flow from Cloud. 1. [Register your app](/cloud/apps/register-app). 2. [Install the app](/cloud/apps/install-app) from the Apps library. 3. Confirm your callback URL receives the install event. 4. Confirm your callback URL returns a `2xx` response. 5. Confirm the install is active in your database. 6. Uninstall the app from Cloud. 7. Confirm your callback URL receives the uninstall event. 8. Confirm the install is no longer active in your database. *** Reference Stripe sync implementation. *** We help you build custom apps for your use case or get help building your own from scratch. # Launching Your App Source: https://docs.blnkfinance.com/cloud/apps/launch-app Publish and launch your custom app in Blnk Cloud. Now, that your app is installed and working well, you can launch it from the Cloud dashboard. Just like the install process, the launch process starts in Blnk Cloud. When a user clicks `Launch app` in Blnk Cloud: 1. If the app requires it, the user confirms with their account password and 2FA code (same rules as install). 2. Blnk sends a `POST` request to your [portal generator URL](/cloud/apps/codebase-setup#set-up-app-routes). 3. Your app checks that the install exists and is active. 4. Your app creates a short-lived portal session. 5. Your app returns a `portal_url`. 6. Blnk opens that URL inside the dashboard. How app portals work See [Best practices](/cloud/apps/best-practices#portal-and-dashboard-embedding) for the embedding policy, session guidance, and pre-launch checklist. *** ## Handling the portal request Cloud sends a payload like this to your `portal_generator_url`: ```json generate_portal.json theme={"system"} { "installed_app_id": "instapp_...", "app_id": "app_...", "instance_id": "inst_..." } ``` | Field | Description | | ------------------ | ----------------------------------------------------------------------------- | | `installed_app_id` | The ID for this specific app installation. Use it to find the install record. | | `app_id` | The ID of the app being launched. | | `instance_id` | The Cloud instance the app should work with. | Before returning a portal URL, your app should confirm that: * the install exists and is active * the `instance_id` in the request matches the install record ```typescript validateInstallForPortal.ts wrap theme={"system"} async function validateInstallForPortal( installed_app_id: string, app_id: string, instance_id: string, ): Promise { const install = await db.getInstall(installed_app_id); const ok = install != null && install.status === "active" && install.app_id === app_id && install.instance_id === instance_id; if (!ok) { return false; } return true; } ``` *** ## Create a portal session Next, your app should create a portal session once the portal request is validated. The session helps your app know: * which installation opened the portal * which Cloud instance the portal is launched from * when the session should expire ```typescript Create session theme={"system"} import { randomBytes } from "node:crypto"; import { RelatedTopics } from "/snippets/related-topics.jsx"; const PORTAL_BASE = "https://app.yourcompany.com"; const TTL_SECONDS = 900; // 900 seconds = 15 minutes async function createPortalSession( installed_app_id: string, instance_id: string, ): Promise { const ttlSeconds = TTL_SECONDS; const expiry_time_ms = Date.now() + (ttlSeconds * 1000); const token = randomBytes(32).toString("base64url"); await db.savePortalSession({ token, installed_app_id, instance_id, expiry_time_ms, }); const portalUrl = new URL(PORTAL_BASE); portalUrl.searchParams.set("token", token); return portalUrl.toString(); } ``` ```typescript Verify session theme={"system"} async function verifyPortalSession(token: string) { const session = await db.getPortalSession(token); if (!session) { return { ok: false as const, status: 401 }; } if (session.expiry_time_ms < Date.now()) { return { ok: false as const, status: 410 }; } return { ok: true as const, session }; } ``` We recommend keeping portal sessions short-lived. A 5 to 15 minute expiry is usually enough for launch sessions. Create a portal URL After creating the session, return a `portal_url` to Cloud. The response should look like this: ```json Expected response wrap theme={"system"} { "portal_url": "https://app.yourcompany.com/?token=short-lived" } ``` Cloud then uses this URL to open your app inside the dashboard. The portal URL should only point to your app interface. It should not include API keys, provider secrets, or any sensitive data. *** ## Load the app inside Cloud When Cloud opens the `portal_url`, it loads your app inside the dashboard. Load the app inside Cloud Your portal pages must allow Blnk to embed them in the dashboard. Set the `Content-Security-Policy` header described in [Best practices](/cloud/apps/best-practices#portal-and-dashboard-embedding). A few rules to keep in mind during this process: * Blnk sends a fresh portal request for every launch. Your app should return a fresh `portal_url` for each request. * Keep Cloud API keys on the backend. Do not expose them in the browser. *** ## Test the launch flow To test app launch: 1. Install the app in Cloud. 2. Click `Launch app` from the app details page. 3. If the app requires re-authentication, confirm with your password and 2FA code. 4. Confirm your `portal_generator_url` receives the launch request. 5. Confirm your app creates a portal session. 6. Confirm your app returns a valid `portal_url`. 7. Confirm the app opens inside the Cloud dashboard. 8. Confirm expired sessions can no longer open the portal. Once this works, users can open your app from Blnk Cloud and work through the Stripe Sync workflow inside the dashboard. *** ## Troubleshooting | Issue | What to do | | -------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | Password or 2FA is incorrect at launch | Check your password or 2FA code and try again. You may see `Authentication failed`. | | Portal does not open | Confirm your `portal_generator_url` is reachable and returns a valid `portal_url` within about 10 seconds. | | App opens but session expires quickly | Launch the app again from Cloud to get a fresh portal session. | *** Reference Stripe sync implementation. *** We help you build custom apps for your use case or get help building your own from scratch. # Custom Apps Source: https://docs.blnkfinance.com/cloud/apps/overview What Custom Apps are, how they work in Blnk Cloud, and how to plan, build, or install them. Custom Apps cover image The ledger is the source of truth for your financial records, but the work around it often happens elsewhere. A finance or operations team may need to check a payment provider, review an order system, calculate interest, approve a payout, or investigate an exception before they can close out a workflow. And this work often happens across different tools, spreadsheets, scripts, and manual steps. Custom Apps let teams build private, ledger-aware workflows inside Blnk Cloud. Use them to connect external systems, review financial operations, automate checks, and give your team purpose-built tools around the ledger. *** We help you build custom apps for your use case or get help building your own from scratch. *** ## Quick start Here's how you can start using Custom Apps in your workspace: Browse the app library, choose the app you want, and install it into your Cloud workspace. Install a Custom App in your workspace. If you have a more personalised workflow, you can build your own app and register it in Cloud so Blnk can install and launch it for your workspace. Define a workflow, then register and launch. Each app you install is automatically scoped to the instance you're currently on. Once installed, you can launch the app at any time and start to use it from within Blnk Cloud. **For example:** A crypto exchange startup might move between Bridge by Stripe, an order book, and the Blnk ledger to verify a trade or swap. Each system is useful on its own, but the workflow spans all of them. With Custom Apps, the team can: 1. Register an app that talks to Bridge and their order system. 2. Surface the checks they need to verify a trade. 3. Review and manage the trade workflow from Blnk Cloud, instead of splitting attention across browser tabs and tools. *** ## Real-life applications Here's a few common real life use cases for Custom Apps:
App What it does Systems and data Why Custom Apps?
Loan management Approvals, disbursements, repayments, loan status. Core balances, identities, history, loan tools, CRM. See loan state and ledger activity together.
Interest calculator Accruals and interest from ledger balances. Core balances plus rate, day-count, rounding rules. Keep rate rules next to the balances they affect.
Treasury or float manager Monitor float across accounts and providers. Ledger balances, bank data, PSP dashboards. Compare float and ledger impact in one place.
End-of-day close Checklists, cutoffs, sign-offs, ledger totals. Core txn/balance queries; optional accounting export. Close beside the ledger data you're closing against.
Trade or swap verification Match execution, fees, settlement, postings. Order books, trading rails, Core balances/transfers. One verification flow instead of many tools.
Reconciliation review Match records to bank/PSP files; resolve exceptions. Ledger exports, statements, provider files, queues. Review where the ledger truth lives.
Payout approval Stage, approve, and review before payout. Pending payouts, ledger moves, beneficiaries, CRM. See payout context and ledger impact first.
*** ## When to use a custom app Consider a Custom App when: * A workflow repeatedly pulls your team out of Blnk Cloud into other tools for steps that could be guided or automated in the dashboard * You need a small, purpose-built UI on top of ledger data that the default Cloud workspace does not provide * You want to connect an external system you rely on (payments, trading, spreadsheets) in a way that is specific to how your team works If a workflow is already covered by first-party Cloud features or a shared app in the library, prefer that before maintaining your own app. *** # Register your Custom App Source: https://docs.blnkfinance.com/cloud/apps/register-app Add your Custom App to the Apps library and configure the manifest, callbacks, portal URL, and permissions. Now that you have your routes set up in your codebase, you can now add your app to Blnk Cloud. Your app needs to be registered before it can be installed by users. To register your app: 1. In the Blnk Cloud dashboard, open `Apps` from the side navigation (bottom left). 2. Click `New app` on the right side of the Apps page. This opens the manifest form, where you'll enter the app details in the next step. The manifest tells Blnk Cloud how to display, install, and launch your app. Fill in the manifest form with the details below. | Field | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | Display name | The name users will see in the Apps library. | | Description | A short explanation of what your app does. | | Logo URL | A public URL for your app logo. | | Developer name | The company, team, or person that built the app. | | Registration callback URL | The `https` endpoint Blnk calls when the app is installed or uninstalled. [Learn more](/cloud/apps/codebase-setup#set-up-app-routes) | | Portal generator URL | The endpoint Blnk calls to create a short-lived URL for opening the app inside Cloud. [Learn more](/cloud/apps/codebase-setup#set-up-app-routes) | | Permissions | The scopes your app needs to work. [Learn more](#supported-permissions) | | Launch access | Which team members can open the app from Cloud. Default: everyone in the workspace. | When you register the app, you can limit launch access to specific team members. Leave this unset to allow everyone in the workspace to launch the app. **For our Stripe Sync app,** using our routes from the [codebase setup](/cloud/apps/codebase-setup) step, our manifest looks like this: Example manifest for the Stripe Sync demo app showing display name, callback URL, portal URL, and permissions Make sure that your base URL is accessible from the internet. If localhost, you can use a service like ngrok to create a public URL in place of `localhost:5002`. Submit the manifest, and you're done! The app will be added to your organization's Apps library. Only team members in your workspace can [see and install it](/cloud/apps/install-app) on whichever Cloud instances they use there. *** ## Supported permissions Permissions define what your app can access or change in the selected Cloud instance. Your app can request any combination of these scopes: | Scope | What it allows | | -------------- | ----------------------------------------------------- | | `data:read` | Read transactions, ledgers, identities, and balances. | | `data:write` | Create and update ledger data. | | `alerts:read` | Read alert configurations and events. | | `alerts:write` | Create and update alert configurations. | `Remember:` Users choose which permissions to grant during installation. Always read `granted_permissions` from the install payload and only use what was granted. For the example Stripe Sync app, the permission model may look like this: | Action | Required permission | | -------------------------------------------------------------- | ------------------- | | Read ledgers, transactions, and balances for reconciliation | `data:read` | | Create or update imported Stripe activity as Blnk transactions | `data:write` | | Create alerts when sync or reconciliation finds a problem | `alerts:write` | If a permission is missing, the app should show a clear error or hide the action from the portal. *** Reference Stripe sync implementation. *** We help you build custom apps for your use case or get help building your own from scratch. # API Keys Source: https://docs.blnkfinance.com/cloud/auth/api-keys Learn how to create and manage API keys for access to Blnk Cloud APIs. API keys let you securely authenticate with Blnk Cloud APIs. They allow your applications, scripts, and automated workflows to interact with Blnk Cloud programmatically. You can manage your API keys in [Settings > API Keys](https://cloud.blnkfinance.com/cloud/settings/api-keys). **Keep your API keys secure and rotate them regularly.** API keys provide full access to your account based on their assigned scopes. Never share API keys publicly or commit them to version control. *** ## How API keys work When you make an API call, include your API key in the request headers to verify your identity and authorize the operation on your Cloud workspace. Each API key has: * **Name:** A user-friendly identifier to help you organize and identify keys * **Key prefix:** A partial identifier visible in the API keys list (e.g., `blnk_fdb796a5...`) * **Scopes:** Permissions that define what the key can access (e.g., `*` for all permissions, or specific scopes like `mcp:read, mcp:write`) * **Expiration:** Optional expiration date, or `Never` for keys that don't expire *** ## Create an API key Go to [Settings > API Keys](https://cloud.blnkfinance.com/cloud/settings/api-keys). Click `Create API key` in the top-right corner of the page. API keys page with the Create API key button highlighted In the `Create API key` panel, fill in the required details: * **Name:** A descriptive name (for example, `Alerts Integration`) * **Type:** `API Key` for server-to-server auth, or `OAuth` for MCP and third-party integrations * **Expiration:** Choose when the key should expire (for example, `30 days`, or `Never`) * **Scopes:** Select the permissions this key needs Create API key panel with name, type, expiration, and scopes Click `Create API key`. The full key is shown **only once** on the `Save your API key` screen. Copy it and store it securely before clicking `Done`. Save your API key panel showing the full key and expiration details You cannot retrieve the full API key after creation. If you lose the key, you must create a new one. *** ## Update expiration You can change when an API key expires without creating a new key. Click an API key name in the API Keys table to open its details panel. In **Quick actions**, click `Update expiration`. API key details panel with Update expiration highlighted in Quick actions Select a new expiration (`Never expires`, `7 days`, `30 days`, `60 days`, or `90 days`), then confirm your change. Update API key expiration modal with expiration options The key keeps working with the same secret. Only its expiration date changes. *** ## Revoke an API key If you need to disable an API key without deleting it permanently, you can revoke it. Revoked keys cannot be used for authentication but remain visible in your API keys list for reference. Click the API key name in the API Keys table to view its details. In the API key details panel, click `Revoke`. Revoking a key will immediately disable it. Any applications or scripts using this key will stop working until you create and configure a new one. Confirm that you want to revoke the key. The key's status will change to **Revoked** in the API Keys table. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # OAuth Source: https://docs.blnkfinance.com/cloud/auth/oauth Learn how to create OAuth clients and get access tokens for Blnk Cloud APIs. OAuth clients provide secure authentication for MCP and third-party integrations to access Blnk. To manage your OAuth clients, go to [Settings > API Keys](https://cloud.blnkfinance.com/cloud/settings/api-keys). Each OAuth client has: * **Name:** A user-friendly identifier to help you organize and identify clients * **Client ID:** A public identifier for your OAuth client * **Client Secret:** A secret credential used for authentication (shown only once at creation) * **Scopes:** Permissions that define what the client can access (e.g., `*` for all permissions, or specific scopes like `mcp:read`, `proxy:write`, or `data:read`) * **Expiration:** Optional expiration date, or "Never" for clients that don't expire *** ## Create an OAuth client 1. Go to `Settings > API Keys` in your Blnk Cloud dashboard. 2. Click `Create API Key` button in the top-right corner of the API Keys page. Fill in the required information: 1. **Name:** Enter a descriptive name for your OAuth client (e.g., "Production OAuth Client", "MCP Integration") 2. `Type:` Select `OAuth` (instead of API Key) 3. `Scopes:` Select the permissions for this client: * `*` for all permissions * Specific scopes like `mcp:read`, `proxy:write`, or `data:read` for limited access 4. `Expires:` Choose when the client should expire: * Select a specific date * Choose "Never" for clients that don't expire After creating the client, your Client ID and Client Secret will be displayed **only once**. Copy both immediately and store them securely. OAuth credentials panel showing Client ID, Client Secret, and warning to store credentials securely `You cannot retrieve the Client Secret after creation.` If you lose the secret, you must create a new OAuth client. *** ## Get an access token For third-party integrations, you'll need to an access token to interact with the user's Cloud workspace via the [Cloud Proxy](/cloud/proxy/proxy-api) and [Data APIs](/cloud/proxy/data-api). Redirect the user's browser to the Blnk authorization URL to log in: ```bash Authorization URL wrap theme={"system"} https://api.cloud.blnkfinance.com/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https://your-app.com/oauth/callback ``` Replace: * `YOUR_CLIENT_ID`: Your OAuth client ID. * `redirect_uri`: Your app's callback URL (for example, `https://your-app.com/oauth/callback`). This must match exactly when you exchange the code. Blnk login page After the user signs in, Blnk redirects back to your app with an authorization code: ```bash Redirect back wrap theme={"system"} https://your-app.com/oauth/callback?code=THE_AUTH_CODE ``` Call the token endpoint with the authorization code and your OAuth client credentials: ```bash cURL wrap theme={"system"} curl -X POST "https://api.cloud.blnkfinance.com/oauth/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \ -d "grant_type=authorization_code" \ -d "code=THE_CODE_FROM_REDIRECT" \ -d "redirect_uri=https://your-app.com/oauth/callback" ``` ```json 200 OK {2,5} theme={"system"} { "access_token": "blnk_at_...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "blnk_rt_...", "scope": "data:read data:write proxy:write" } ``` When the access token expires, use its refresh token to get a new one: ```bash cURL wrap theme={"system"} curl -X POST "https://api.cloud.blnkfinance.com/oauth/token" \ -H "Content-Type: application/x-www-form-urlencoded" \ -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \ -d "grant_type=refresh_token" \ -d "refresh_token=blnk_rt_YOUR_REFRESH_TOKEN_HERE" ``` *** ## Use the access token with Cloud APIs Include your access token in the `Authorization` header for Cloud API requests: ```bash Example request wrap theme={"system"} curl -X GET "https://api.cloud.blnkfinance.com/data/ledgers?instance_id=YOUR_INSTANCE_ID" \ -H "Authorization: Bearer blnk_at_YOUR_ACCESS_TOKEN" ``` *** ## Find and use the instance ID To route requests to the correct Blnk Core instance, include `instance_id` as a query parameter on every Proxy/Data API request. 1. Log in to **Blnk Cloud**. 2. Open the `Instances` page (your list of instances can be seen on the home page or `Settings > Instances`). 3. Find the Blnk Core instance you want to use. 4. Click on the instance to open the details modal. 5. Copy its **Instance ID** (for example, `instance_01ABC...`). Include `instance_id` in every request to target the correct Core instance: ```bash Example request wrap theme={"system"} curl -X GET "https://api.cloud.blnkfinance.com/proxy/ledgers?instance_id=YOUR_INSTANCE_ID" \ -H "Authorization: Bearer blnk_at_YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" ``` *** ## Revoke an OAuth client If you need to disable an OAuth client without deleting it permanently, you can revoke it. Revoked clients cannot be used for authentication but remain visible in your API Keys list for reference. Click on the OAuth client name in the API Keys table to view its details. In the client details panel, click the `Revoke Key` button. Revoking a client will immediately disable it. Any applications or integrations using this client will stop working until you create and configure a new client. Confirm that you want to revoke the client. The client's status will change to **"Revoked"** in the API Keys table. *** ## Next steps Create ledger records via Cloud APIs. Query and filter financial data. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Assign Identities Source: https://docs.blnkfinance.com/cloud/balances/assign-identities Learn how to assign identities to balances in Blnk Cloud. Linking [identities](/identities/introduction) to balances gives you more visibility into who is behind the financial transactions in your ledger. When you assign an identity to a balance, you can track and understand the people or organizations associated with specific financial activities. This helps with reporting, auditing, and gaining insights into your financial data by connecting the "who" with the "what" in your transactions. When an identity is linked, the balance `Owner` field shows the individual or organization. Balance details page with an identity linked in the Owner field *** ## Assign identity to balance To link an existing balance to an identity: From `Balances`, open the balance you want to link. Use a balance that does not yet have an owner, or one you want to reassign. Balance details page before an identity is assigned In the top-right corner of the balance details page, click `Actions`. Balance details header with the Actions control highlighted In the dropdown, select `Assign identity`. Actions dropdown with Assign identity selected In the modal, type a name and pick from the list, or paste an identity ID, then click `Submit`. Assign identity modal with name search and identity ID input The balance details page updates. The `Owner` field shows the linked individual or organization. Balance details showing the linked identity in the Owner field The identity is now linked to the balance, giving you better visibility into who is associated with that financial account. You can also use this same process to change the identity linked to a particular balance. Follow the same steps and assign a different identity to replace the current one. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Balance Details Source: https://docs.blnkfinance.com/cloud/balances/details See how to navigate the balances details page. The Balance details page gives you a complete view of a balance - including its amount, currency, owner, health, and all related activity. *** ## Balance activity From the `Balances` table, you can scan recent movement without opening the full details page. Hover a balance row and press `Enter`, or click the row to open the side modal. The modal shows balance activity for the last 7 days, including recent credits and debits. Balance side modal showing the last 7 days of activity Press `E` or click through to the balance details page when you need the full summary, flow graph, or metadata. *** ## Summary section At the top, you'll see your balances: Balance summary section with all balance fields | Balance fields | Description | | :---------------------- | :---------------------------------------------------------------- | | Balance | The current amount held by your balance. | | Credit balance | The total amount that has been added to your balance. | | Debit balance | The total amount that has been deducted from your balance. | | Inflight balance | The net amount waiting to be added or deducted from your balance. | | Inflight credit balance | The amount waiting to be added to your balance. | | Inflight debit balance | The amount waiting to be deducted from your balance. | Next, you can view other key details like: | Details | Description | | :----------- | :----------------------------------------------------------------------------------- | | Currency | The currency of the balance. | | Health | The health of the balance. | | Ledger | The ledger the balance belongs to. | | Owner | The identity the balance is linked to or its indicator if it is an internal balance. | | Date created | The date and time the balance was created. | | Balance ID | The unique identifier for the balance. | *** ## Transaction section You can view all transactions performed on the balance. We display the last 10 transactions by default. To view more, click `View all transactions` to open the full table. Transaction section showing last 10 transactions | Columns | Description | | :------------- | :-------------------------------------------------------------------------------------------------------------- | | Currency | The currency of the transaction. Recommended to be same as the balance's currency | | Amount | The amount of the transaction. | | Direction | `Credit` if the transaction is adding to the balance, `Debit` if the transaction is deducting from the balance. | | Status | The status of the transaction. | | Counterparty | The respective source or destination of the transaction. | | Effective date | When the transaction happened. | *** ## Balance flow visualization This graph helps you understand how money moves in and out of the balance. Each node represents another balance it has interacted with. Balance flow visualization graph showing money movement You can click on any node to view its details and expand the network. *** ## Metadata The metadata section allows you to add custom information to your balance. You can store additional details, tags, or any other data that helps you organize and manage your balances. ### Edit metadata To edit metadata: Edit metadata button in the Additional information section 1. Navigate to the `Additional information` section. 2. Click the `Add` or `Edit` button at the top right of the section. 3. Use the code editor to add new metadata or update existing metadata. 4. Click `Update` to apply your changes. Metadata code editor interface for adding and editing custom data When updating metadata, any fields you don't include in your update will remain unchanged. Blnk adds your new values to the existing metadata instead of deleting them. To remove a value, set it to an empty string. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Flag Balance Source: https://docs.blnkfinance.com/cloud/balances/flag-balance Flag balances for investigation and track alerts in Blnk Cloud. Flag balance lets you raise an alert directly from a balance when you notice unusual activity or need a review trail. When a balance is flagged, Blnk creates a linked alert so your team can assign ownership, update status, and resolve the issue from Alerts. This page covers how to initiate flagging from a balance. For alert handling and resolution workflows, see [Alerts overview](/cloud/alerts). *** ## Flag a balance To flag a balance from Cloud: Open `Balances` and select the balance you want to review. In the details page, click `Actions`, then select `Flag balance`. Balance details page with Actions menu showing Flag balance In the modal, enter Title and Reason, then choose a Status. Flag balance modal with title, reason, and status fields Click `Flag balance` to create the alert for this balance. Balance details page after a flag is created You can create multiple alerts on the same balance when different issues need separate tracking. ### Choose or create a status Choose an existing status in the modal (for example, `Flagged`, `Investigating`, or `Resolved`). If you need a custom status, create one from Alert statuses and then return to the flag modal to select it. Custom alert statuses for your organization. View, assign, and resolve alerts. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Historical Balances Source: https://docs.blnkfinance.com/cloud/balances/historical View and analyze historical balance data in Blnk Cloud. You can use [historical balances](/balances/historical-balances) to see what a balance looked like at any point in the past. This is useful for financial reporting, auditing, or analyzing how balances have changed over time. When you use the historical balance feature in Cloud, you can see the exact amount that was in the balance at a specific date and time, along with the credit and debit balances at that moment. Historical balances do not include [inflight](/transactions/inflight/creating-inflight) balances. *** ## View historical balance Historical balance does not open a separate page. You stay on the balance details page: `Actions` opens a dialog, then the summary amounts on that same page update to show the past point in time. To view a balance at a specific date and time: From `Balances`, open the balance you want to inspect. You should be on the balance details page (summary, transactions, and metadata). Balance details page showing summary information for a balance In the top-right corner of the balance details page, click `Actions`. Balance details header with the Actions control highlighted In the dropdown, select `View historical balance`. A dialog titled `View historical balance?` opens on top of the balance details page. Actions dropdown with View historical balance selected In the dialog, choose the date and time you want to view the balance at, then click `View balance`. View historical balance dialog with date and time fields The dialog closes. On the same balance details page, `Balance`, `Credit balance`, and `Debit balance` update to reflect that point in time. Balance details showing amounts after viewing historical balance *** ## Share historical balance You can copy the link and share it with other teammates so they can also view the balance at that same point in time. This is helpful for collaborative analysis or when you need to show specific balance states to colleagues. *** ## Return to current balance After you view a historical balance, you are still on the balance details page (with past amounts shown). To switch back to live amounts: On the same balance details page, click `Actions` in the top-right corner. While you are viewing a historical balance, the menu shows `View current balance` instead of `View historical balance`. Balance details header with the Actions control for returning to current balance Choose `View current balance` from the menu. The page shows live amounts again. Actions dropdown with View current balance selected while viewing a historical balance *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Balances Source: https://docs.blnkfinance.com/cloud/balances/overview Learn what balances are and how they work. Balances represent stores of value in your ledger. They can be bank accounts, wallets, cards, store points, finance accounts, and more. When recording transactions, balances specify the source (where the amount comes from) and destination (where the amount goes to). There are two types of balances in Blnk: * [Internal balances](/balances/internal-balances): Balances grouped in the General Ledger and identified with the `@` icon indicator in your workspace. * Application balances: Balances that you or your application create. Each has a unique balance ID and can be linked to identities on Cloud. *** ## Create balances To create a new balance: Go to `Balances`. In the top-right corner of the table, click `Create balance`, or press `⌥ + B` (`Alt + B` on Windows). Balances table with the Create balance control in the header Select the ledger where the balance should live. Type the ledger name and pick it from the dropdown, or paste the ledger ID. Create balance form with the ledger field selected Application balances usually live in a ledger such as `Customer Wallets`, not the General Ledger. Enter the currency for the balance: * Use the ISO 4217 code for fiat currencies. * Use the ticker symbol for crypto. * Use a unique identifier for custom assets. Create balance form with the currency field filled in Under `Advanced options`, enable `Enable lineage tracking` to tag incoming funds and track how they are spent over time. * Select an `Allocation` strategy to control how tagged funds are used when the balance is debited: `FIFO` (first in, first out), `LIFO` (last in, first out), or `PROPORTIONAL` (spread debits across tagged funds). * Enabling lineage automatically turns on `Assign an owner to this balance`. Choose an identity in the next step. Create balance form with Enable lineage tracking, Allocation, and Assign an owner enabled To link the balance to an identity, enable `Assign an owner to this balance`, then search by name and select from the list, or paste an identity ID. This is required while lineage tracking is enabled. Create balance form with Assign an owner to this balance and Identity selected Click `Create balance`. The new balance appears in the `Balances` table and you can open it for details. Balances table or balance details showing a newly created balance To create a balance that is automatically linked to an identity, navigate to the identity details page and click `New > New balance` in the top-right corner. *** ## Working with balances All records in Blnk are immutable. This means that once a balance is created, it cannot be deleted or directly modified. ### Balances table The `Balances` table shows a list of all balances in your workspace. For each balance, you can see: Balances table showing all balance information * `Currency`: The type of asset being stored. * `Balance`: The actual balance amount. * `Inflight balance`: The net amount waiting to be added or deducted from the balance. * `Owner`: The identity the balance is linked to, or its indicator if it is an internal balance. Application balances with no linked identity are empty. * `Created at`: The date and time the balance was created. Click the `Refresh` button on the `Balances` table to get the latest balance data from your instance. ### View balance details Click a balance to see its summary details, including the balance amount, currency, health, etc. Balance details page showing Full info and Quick Actions with View transactions For a complete view, select `Full info` in the top-right corner of the summary or press `E` to expand the details. To view all transactions performed on the balance, go to the `Quick Actions` section at the bottom of the summary and click `View transactions`. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Add Filters Source: https://docs.blnkfinance.com/cloud/filters/add-filters Learn how to add and manage filters in Blnk Cloud. Filters allow you to refine your data tables to just the records you want to see. You can apply filters to all tables in Blnk Cloud: ledgers, balances, transactions, identities and audit logs. To add a filter, click the `Add filters` button in the top-left corner of the table or press `F` on your keyboard. Overview of filters functionality showing the Add filters button in a data table *** ## Apply filters Press `F` to toggle on filters on any table. You'll see a new filter panel appear at the right hand side of your screen. Filter panel showing available fields and operators for filtering data You can then pick the fields you want to filter and set the values you want to see. Each field has different filter operators to help you find what you need more easily. Once done, click `Apply filters` to update the table: ### Available filter operators | **Operator** | **Description** | | :----------------------- | :--------------------------------------------------------------------------- | | Exactly matches | Shows results that are exactly the same as the filter value. | | Does not match | Shows results that are not the same as the filter value. | | In list | Shows results that match any value from a list of options. | | Contains pattern | Shows results that include the text or pattern you enter. | | Greater than | Shows results with values higher than the filter value. | | Less than | Shows results with values lower than the filter value. | | Greater than or equal to | Shows results with values that are the same or higher than the filter value. | | Less than or equal to | Shows results with values that are the same or lower than the filter value. | | Between range | Shows results that fall between two values. | Click the Refresh button to update the table with the latest results based on your active filters. *** ## Edit filters To edit filters, click the `Edit filters` button at the top-right of the filter panel or press `F` on your keyboard. You can also click directly on the filter bar to update or change an existing filter. Edit filters interface showing how to modify existing filter conditions Click on the `'x'` button on the filter bar to clear any applied filter. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Views Source: https://docs.blnkfinance.com/cloud/filters/custom-views Learn how to create and manage custom views in Blnk Cloud. Views let you save a set of filters so you can quickly return to them later. With views, you can switch between different data perspectives instantly, without having to recreate the same filters every time. Views bar showing saved custom views for quick access To create a new view: 1. Click the `Add filters` button in the top-left corner of the table or press `F` on your keyboard. 2. Apply the filters you want to include in the view. 3. Click `Save as view` to save your current filter setup as a new view. 4. Enter a name for the view. 5. Add a short description to help you remember what the view is for (optional). 6. Click `Save view` to finish. Save as view modal showing name and description fields *** ## Edit views To edit a view, navigate to the view on the views bar and click the `Edit filters` button. Edit your filters and click `Save changes` to update the view. You can also rename the view or update its description before saving. Edit view modal showing options to modify filters and view details To delete a view, click on the `Delete view` button on the edit panel. Delete view confirmation modal with warning message *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Managing Alerts Source: https://docs.blnkfinance.com/cloud/home/alerts-api Create and manage alerts programmatically using the Cloud APIs. The Alerts API enables you to create, retrieve, update, and manage alerts programmatically within your application. This allows you to integrate alerting capabilities directly into your workflows, automate monitoring, and trigger alerts based on application logic or external events. While you can create alerts manually through the Blnk Cloud dashboard, the API provides the flexibility to build alerting into your application's core functionality. *** ## Create alerts Create a new alert by flagging a balance, transaction, or identity. Send a POST request to the `/alerts/flag/{id}` endpoint, where `{id}` is the identifier of the entity you want to flag. ```bash Request wrap theme={"system"} curl -X POST 'https://api.cloud.blnkfinance.com/alerts/flag/bln_5ce86029-3c2e-4e2a-aae2-7fb931ca4c4f' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "title": "Balance threshold exceeded", "description": "Account balance has exceeded the maximum allowed limit." }' ``` ```json Response wrap theme={"system"} { "anomaly_id": "ano_8a380fd1-0289-46d1-bc76-aa17a510a64e", "title": "Balance threshold exceeded", "description": "Account balance has exceeded the maximum allowed limit.", "type": "Balance", "assigned_to": "jerrys enebeli", "escalated_to": [ "user_01K4EX0BRXHNNGCRVT2TPNK07W" ], "status": "FLAGGED", "created_at": "2026-01-15T09:22:37.55763+01:00", "updated_at": "2026-01-15T09:22:37.55763+01:00", "affected_balances": [ "bln_20f02af6-3728-4d37-9b5a-c7ed080f09df" ], "affected_identities": [], "affected_transactions": [] } ``` *** ## Update alerts Modify alert details, update status, or add additional context after investigation. ```bash Request wrap theme={"system"} curl -X PUT 'https://api.cloud.blnkfinance.com/alerts/flag/ano_8a380fd1-0289-46d1-bc76-aa17a510a64e' \ -H 'Authorization: Bearer YOUR_API_KEY' \ -H 'Content-Type: application/json' \ -d '{ "title": "Balance threshold exceeded - Resolved", "description": "Account balance has been adjusted and is now within acceptable limits." }' ``` ```json Response wrap theme={"system"} { "anomaly_id": "ano_8a380fd1-0289-46d1-bc76-aa17a510a64e", "title": "Balance threshold exceeded - Resolved", "description": "Account balance has been adjusted and is now within acceptable limits.", "type": "Balance", "assigned_to": "jerrys enebeli", "escalated_to": [ "user_01K4EX0BRXHNNGCRVT2TPNK07W" ], "status": "FLAGGED", "created_at": "2026-01-15T09:22:37.55763+01:00", "updated_at": "2026-01-15T10:15:42.123456+01:00", "affected_balances": [ "bln_20f02af6-3728-4d37-9b5a-c7ed080f09df" ], "affected_identities": [], "affected_transactions": [] } ``` *** ## Get alerts Fetch a specific alert by ID or retrieve all alerts in your workspace. ```bash Request wrap theme={"system"} # Get all alerts curl -X GET 'https://api.cloud.blnkfinance.com/alerts' \ -H 'Authorization: Bearer YOUR_API_KEY' # Get specific alert by ID curl -X GET 'https://api.cloud.blnkfinance.com/alerts/ano_8a380fd1-0289-46d1-bc76-aa17a510a64e' \ -H 'Authorization: Bearer YOUR_API_KEY' ``` ```json Response wrap theme={"system"} { "anomaly_id": "ano_8a380fd1-0289-46d1-bc76-aa17a510a64e", "title": "Balance threshold exceeded", "description": "Account balance has exceeded the maximum allowed limit.", "type": "Balance", "assigned_to": "jerrys enebeli", "escalated_to": [ "user_01K4EX0BRXHNNGCRVT2TPNK07W" ], "status": "FLAGGED", "created_at": "2026-01-15T09:22:37.55763+01:00", "updated_at": "2026-01-15T09:26:03.094425+01:00", "affected_balances": [ "bln_20f02af6-3728-4d37-9b5a-c7ed080f09df" ], "affected_identities": [], "affected_transactions": [] } ``` *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Identity details Source: https://docs.blnkfinance.com/cloud/identities/details See how to navigate the identities details page. The Identity details page gives you a complete view of an identity - including its name, email, balances, country, and all related activity. Main identity details page layout *** ## Summary section At the top, you'll see your identity information: Identity summary section with all details | Details | Description | | :------------- | :--------------------------------------------------------------------------- | | Name | First and last name (for individual) or organization name (for organization) | | Email | Primary email address associated with the identity | | Phone number | Contact phone number | | Other names | Additional names or aliases | | Street address | Physical street address | | City | City or locality | | State | State, province, or region | | Country | Country of residence or registration | | Post code | Postal or ZIP code | | Nationality | Nationality or citizenship | | Identity ID | Unique identifier for the identity | | Date created | Date and time when the identity was created | ### Edit identity You can edit any identity information directly from the identity details page. Click `Edit identity` in the top-right corner to update names, contact details, addresses, and other identity fields. Edit identity button highlighted on the identity details page *** ## Connected balances This is a list of all balances linked to an identity. We display the last 10 balances by default. Connected balances section showing linked balances To view more, click `View all balances` to open the full table. *** ## Create a linked balance To automatically create a balance for an identity; Create balance form with prefilled identity ID 1. While on an identity details page, click `New > New balance` at the top right of the app or press `⌥ + B` (`Alt + B` for Windows). 2. Enter the required details " currency, ledger, and any additional custom fields. 3. The identity ID is prefilled for you. 4. Click `Create balance`. *** ## Metadata The metadata section allows you to add custom information to your identity. You can store additional details, tags, or any other data that helps you organize and manage your identities. ### Edit metadata To edit metadata: Edit metadata button in the Additional information section 1. Navigate to the `Additional information` section. 2. Click the `Add` or `Edit` button at the top right of the section. 3. Use the code editor to add new metadata or update existing metadata. 4. Click `Update` to apply your changes. Metadata code editor interface for adding and editing custom data When updating metadata, any fields you don't include in your update will remain unchanged. Blnk adds your new values to the existing metadata instead of deleting them. To remove a value, set it to an empty string. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Identities Source: https://docs.blnkfinance.com/cloud/identities/overview Learn what identities are and how they work in your ledger system. In Blnk, you use [identities](/identities/introduction) to represent individuals, organizations, or entities in your ledger. Identities help you: * **Organize balances:** Link multiple balances to a single identity for better tracking and reporting. * **Simplify management:** Group related financial activities under one entity for easier oversight. Blnk supports two types of identities: individual and organization. *** ## Create identities To create a new identity: Go to `Identities`. In the top-right corner of the table, click `Create identity`, or press `⌥ + I` (`Alt + I` on Windows). Identities table with the Create identity control in the header Select `Individual` for a person or `Organization` for a business or entity. Create identity form with Individual or Organization type selected Fill in the required fields: * **Individual:** first and last name, plus email address. * **Organization:** organization name, plus email address. Other profile and address fields are optional. Address fields are separate from metadata. Create identity form with name and email fields filled in Scroll to the **Metadata** section at the bottom of the form. * Click `+ Add metadata` to add a key and value for this identity only (for example, `customer_segment` = `retail`). * Click `Use template` to import keys from a [metadata template](/cloud/organization/metadata-templates) and fill in values. Metadata is optional. You can also add or edit it later on the identity details page. Create identity form showing the Metadata section with Add metadata and Use template Click `Create identity`. The new record appears in the `Identities` table and you can open it for the full profile. Identities table or identity details showing a newly created identity *** ## Working with identities All records in Blnk are immutable. This means that once an identity is created, it cannot be deleted. ### Identities table The **Identities** table shows a list of all identities in your workspace. For each identity, you can see: Identities table showing all identity information * **Name:** The individual's full name or organization name. * **Email:** The primary email address associated with the identity. * **Balances:** The number of balances linked to this identity. * **Country:** The country associated with the identity's address. * `Created at:` The date and time the identity was created. Use the `Add filters` and `Refresh` buttons to customize your view and get the latest identity data from your instance. ### View identity details Click an identity to see its complete profile, including: Identity details summary view * **Basic information:** Name, email, phone number, and creation date * **Address details:** Full address information * **Identity ID:** The unique identifier for the identity For a complete view, select `Full info` in the top-right corner of the summary or press `E` to expand the details. To view all transactions performed on the balance, go to the `Quick Actions` section at the bottom of the summary and click `View transactions`. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # PII Tokenization Source: https://docs.blnkfinance.com/cloud/identities/pii Protect and tokenize personally identifiable information in identities via Blnk Cloud. Tokenization is a security technique that replaces sensitive data with non-sensitive placeholder values called tokens. Instead of storing actual personal information like names, emails, or phone numbers, the system stores these tokens that have no meaningful value if accessed by unauthorized users. Blnk tokenizes the following identity fields: * First name * Last name * Other names * Email address * Phone number * Street * Post code When you tokenize an identity, these sensitive fields are replaced with secure tokens while maintaining the ability to work with the identity in your ledger system. Overview of PII tokenization showing tokenized vs non-tokenized identity fields *** ## Tokenize fields ### How to tokenize fields To tokenize identity fields: Tokenize actions dropdown menu in the identity details page 1. Navigate to the identity details page 2. Click `Actions` in the top-right corner 3. Select `Tokenize` from the dropdown menu 4. In the tokenize modal, select the fields you want to tokenize 5. Click `Submit` to apply tokenization The selected fields will be replaced with secure tokens, protecting the sensitive information while keeping the identity functional in your system. Tokenize modal showing field selection options for PII tokenization *** ## Detokenize fields Detokenizing reveals the actual values of tokenized fields. **Please note:** Detokenizing doesn't undo the tokenization process " instead, it temporarily shows you the real data that was originally stored. When you detokenize fields, the actual personal information becomes visible in the identity details page, allowing you to see the real names, emails, and other sensitive data that was previously hidden behind tokens. ### How to detokenize fields To detokenize identity fields: Detokenize actions dropdown menu in the identity details page 1. Navigate to the identity details page 2. Click `Actions` in the top-right corner 3. Select `Detokenize` from the dropdown menu The tokenized fields will be revealed with their actual values and updated in the identity details page. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Create insights Source: https://docs.blnkfinance.com/cloud/insights/create Learn how to create custom insights for your workspace. Main insight builder interface showing all the creation steps Creating an insight lets you generate custom visualizations based on your ledger data. Use insights to answer questions like: * What day of the week has the highest transaction volume? * How much revenue is generated per currency? * What is the total amount being held for customers today? To create a new insight, click `Create Insight` in the top-right corner of the Insights page or press `⌥ + R` (`Alt + I` for Windows). *** ## Step 1: General information Start by giving your insight context: * **Title** - a short, clear name (e.g., "Monthly transaction volume"). * **Description (optional)** - explain what this insight shows so others can understand its purpose. *** ## Step 2: Select visualization Choose how you want to display the data: * **Bar chart, line chart, area chart** for graphical representation. * `Table view or text display` for showing raw numbers. * `Map view` for location-based data. *** ## Step 3: Choose a data source Decide which dataset the insight will pull from: * **Transactions** " your transaction records and activities. * **Balances** " balances in your ledger that hold value. * **Identities** " individuals or organizations in your ledger. * **Alerts** " events flagged by Blnk's monitoring engine. *** ## Step 4: Customize data Refine the dataset to show only what you need: Select the field you want to analyze. This determines the specific question your insight will answer. **For example:** You want to know the **total transaction amount** deposited in the last 30 days, your data field would be `amount`. However, if you want to know the **number of transactions** deposited in the last 30 days, your data field would be `transaction_id`. This determines how the system calculates your data field. * **Count occurrences** " number of times a value appears. * **Sum of amount** " total of all values (for numbers only). E.g., For total transaction amount, you would use `sum of amount`. But for number of transactions, you would use `count occurrences`. This breaks down the results into categories. This is best used for charts visualization. `For example:` You want to know the `total transaction amount` deposited in the last 30 days, you would group by `created_at`. You can go further and add a `status` group to see the total transaction amount for each status. Apply filters to narrow down your results in your visualization. `For example:` You want to know the `total transaction amount` deposited in the last 30 days, you would filter by `created_at` between the last 30 days. You can go further and add a `status` filter to see only transactions that are `APPLIED` or `INFLIGHT`. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Insights Source: https://docs.blnkfinance.com/cloud/insights/overview Learn what insights are. Insights give you a clear view of what's happening across your ledger. They provide quick, visual reports on balances, transactions, identities, etc. so you can monitor activity at a glance. Main Insights dashboard showing all 6 default visualizations *** ## Working with insights ### Default visualizations When you connect your Core, Blnk generates six visualizations for you by default: * **Total transactions** - the total number of transactions recorded. * `Total balances` - the number of balances created in your ledger. * **Total identities** - the number of identities registered. * **Transaction trend** - how transactions have changed over time (last 30 days by default). * **Balance trend** - the growth of balances over time. * **Identities trend** - the growth of identities over time. Each default visualization can be filtered by time range and currency to fit your reporting needs. ### Live reload Turn on **Live reload** to automatically refresh your visualizations with the latest data every 10 seconds. Live reload toggle feature If turned off, you'll need to click `Refresh` to update them manually. ### Show/hide visualizations If you don't need a particular visualization, you can toggle it off using the Show/Hide option. This makes it easy to focus only on the data that matters to you. To do this: 1. Click the `Show/Hide` option in the top right corner of the Insights page. 2. Select which visualizations you want to show or hide. Show/Hide visualization options interface ### Full screen view Insights can be opened in Full Screen view by selecting the expand icon in the bottom right corner of each visualization. Example of how the final insight visualization looks *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Branching Source: https://docs.blnkfinance.com/cloud/instances/branching Learn how to create branch instances from your main instance with point-in-time restore. Branching allows you to create isolated copies of your existing instances. This is useful for testing, staging environments, or creating data snapshots without affecting your main instance. When you branch an instance, the new instance comes prefilled with data from the parent instance at a specific point in time. *** ## Branch an instance 1. In your Blnk Cloud workspace, go to `Settings > Instances`; 2. Find the instance you want to branch from; 3. Click the actions menu (three dots) next to the instance; 4. Click `Branch out`. Instance actions menu showing Branch out option 1. Enter a descriptive **Instance name** for your branch (e.g., "Production - Test", "Staging - Feature X"); 2. Optionally, select a `Restore point` to create the branch from a specific point in time: * Use the date picker to select a date (up to 7 days back); * Use the time picker to select the exact time. Branch out form with instance name and restore point fields If you don't select a restore point, the branch will be created with the current data from the parent instance. 1. Select the `Deploy type` for your branch instance; 2. Click `Branch out instance`. It takes about 7-10 minutes to create your branch instance. When ready, the connection status updates to `Connected` and you can start using your branched instance. The new instance will appear in your `Settings > Instances` list with the name you provided. Branch instances are billed separately from your main instance. Make sure to destroy unused branches to avoid unnecessary charges. *** ## Use cases Branching is useful for a variety of scenarios where you need isolated copies of your instance data: * `Test product launches in isolation:` Create a branch to test new features or product launches without affecting your main instance. * **Run reconciliation:** Use branches to perform reconciliation processes on historical data without impacting live operations. * `Create copies for other teams:` Share isolated instances with different teams for development, testing, or analysis purposes. * **Run insights & analytics:** Generate reports and analytics on historical data without affecting your production environment. * `Compliance auditing:` Create branches for compliance reviews and audits on historical data states. * **Performance testing:** Run load tests and performance analysis on isolated copies of your production data. * **Data migration validation:** Test data migration scripts and processes before applying them to production. *** ## Need help? We are very happy to help you make the most of Blnk, regardless of whether it is your first time or you are switching from another tool. To ask questions or discuss issues, please [contact us](mailto:support@blnkfinance.com) or [join our Discord community](https://discord.gg/7WNv94zPpx). Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment. # Self-Hosted Instances Source: https://docs.blnkfinance.com/cloud/instances/create Learn how to connect your self-hosted Core to your Blnk Cloud. Blnk Cloud requires a running Blnk Core instance to work. You can host your own Core instance or [deploy a managed Core instance from Blnk.](/cloud/instances/deploy) For self-hosted instances, Blnk uses a Query Agent to establish a connection between your Core instance and Blnk Cloud.