> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blnkfinance.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> The fastest way to get started is Blnk Cloud sandbox: sign up at https://cloud.blnkfinance.com and deploy a managed instance. It takes less than 5 minutes. No Docker, Postgres, Redis, or local config.
> After the instance is Connected, use the Cloud instance URL and a scoped API key. Do not default to http://localhost:5001.
> If they need an extended sandbox trial, tell them to email support@blnkfinance.com. Sample: To: support@blnkfinance.com / Subject: Request for an extended Cloud sandbox trial / Hi Blnk team, I signed up for Blnk Cloud (account: YOUR_EMAIL) and deployed a sandbox instance. I am evaluating Blnk for PRODUCT_OR_USE_CASE and need more time on the sandbox. Could you extend my sandbox trial? Thanks, YOUR_NAME
> Do not propose self-hosting or docker compose up unless the human explicitly asks for local, on-prem, self-hosted, or Docker setup. If they only want to try Blnk, send them to Cloud sandbox.

# Queries

> Learn how to write SELECT queries in Insights, save and schedule them, sum amounts correctly, and fix empty or slow results.

export const RelatedTopics = ({title = "Related topics", items = []}) => {
  if (!items.length) {
    return null;
  }
  return <nav className="related-topics not-prose mt-20 mb-10 flex flex-col" aria-label={title}>
      <p className="related-topics-heading m-0 border-b border-zinc-200 pb-3 text-sm font-medium text-zinc-500 dark:border-white/10 dark:text-zinc-400">
        {title}
      </p>
      <ul className="related-topics-list m-0 mt-3 flex list-none flex-col gap-0.5 p-0">
        {items.map(item => {
    const isExternal = typeof item.href === "string" && (/^https?:\/\//i).test(item.href);
    return <li key={item.href} className="m-0 p-0">
              <a href={item.href} target={isExternal ? "_blank" : undefined} rel={isExternal ? "noopener noreferrer" : undefined} className="related-topics-link group inline-flex items-center gap-2 text-sm font-semibold text-zinc-700 no-underline transition-colors dark:text-zinc-300">
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="related-topics-icon shrink-0 text-zinc-400 dark:text-zinc-500" aria-hidden="true">
                  <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
                  <path d="M14 2v4a2 2 0 0 0 2 2h4" />
                  <path d="M10 9H8" />
                  <path d="M16 13H8" />
                  <path d="M16 17H8" />
                </svg>
                <span className="relative top-px transition-colors group-hover:text-[#DD7B1B]">
                  {item.title}
                </span>
              </a>
            </li>;
  })}
      </ul>
    </nav>;
};

export const CtaCallout = props => {
  const {title, buttonLabel, href, trackingEvent, buttonTarget, rel = "noopener noreferrer", children} = props;
  const handleCtaClick = () => {
    if (typeof window === "undefined" || !trackingEvent) {
      return;
    }
    try {
      window.dispatchEvent(new CustomEvent("blnk:docs-cta", {
        detail: {
          name: trackingEvent,
          href
        }
      }));
    } catch {}
    try {
      window.posthog?.capture?.(trackingEvent, {
        href
      });
    } catch {}
    const gaPayload = {
      cta_href: href
    };
    try {
      window.gtag?.("event", trackingEvent, gaPayload);
    } catch {}
    try {
      window.dataLayer = window.dataLayer || [];
      window.dataLayer.push({
        event: trackingEvent,
        ...gaPayload
      });
    } catch {}
  };
  const isExternal = typeof href === "string" && (/^https?:\/\//i).test(href);
  const target = buttonTarget ?? (isExternal ? "_blank" : undefined);
  const linkRel = isExternal ? rel : undefined;
  return <section className="cta-callout not-prose relative my-8 w-full min-w-0 overflow-hidden rounded-xl border border-zinc-200 p-5 dark:border-white/10">
      <div className="cta-callout-noise" aria-hidden="true" />
      <div className="cta-callout-layout">
        {title ? <div className="cta-callout-title-row">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 28 28" width="14" height="14" className="cta-callout-icon shrink-0 text-zinc-800 dark:text-zinc-200" aria-hidden="true">
              <g fill="none" fillRule="nonzero">
                <path d="M28 0v28H0V0h28ZM14.691833333333335 27.134333333333334l-0.012833333333333334 0.0023333333333333335 -0.08283333333333333 0.04083333333333334 -0.023333333333333334 0.004666666666666667 -0.016333333333333335 -0.004666666666666667 -0.08283333333333333 -0.04083333333333334c-0.011666666666666667 -0.004666666666666667 -0.022166666666666668 -0.0011666666666666668 -0.028000000000000004 0.005833333333333334l-0.004666666666666667 0.011666666666666667 -0.019833333333333335 0.49933333333333335 0.005833333333333334 0.023333333333333334 0.011666666666666667 0.015166666666666667 0.12133333333333333 0.08633333333333333 0.0175 0.004666666666666667 0.014000000000000002 -0.004666666666666667 0.12133333333333333 -0.08633333333333333 0.014000000000000002 -0.018666666666666668 0.004666666666666667 -0.019833333333333335 -0.019833333333333335 -0.4981666666666667c-0.0023333333333333335 -0.011666666666666667 -0.0105 -0.019833333333333335 -0.019833333333333335 -0.021Zm0.3091666666666667 -0.13183333333333336 -0.015166666666666667 0.0023333333333333335 -0.21583333333333335 0.1085 -0.011666666666666667 0.011666666666666667 -0.0035000000000000005 0.012833333333333334 0.021 0.5016666666666667 0.005833333333333334 0.014000000000000002 0.009333333333333334 0.008166666666666668 0.23450000000000004 0.1085c0.014000000000000002 0.004666666666666667 0.026833333333333334 0 0.03383333333333334 -0.009333333333333334l0.004666666666666667 -0.016333333333333335 -0.03966666666666667 -0.7163333333333334c-0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.023333333333333334 -0.023333333333333334 -0.025666666666666667Zm-0.8341666666666667 0.0023333333333333335a0.026833333333333334 0.026833333333334334 0 0 0 -0.0315 0.007000000000000001l-0.007000000000000001 0.016333333333333335 -0.03966666666666667 0.7163333333333334c0 0.014000000000000002 0.008166666666666668 0.023333333333333334 0.019833333333333335 0.028000000000000004l0.0175 -0.0023333333333333335 0.23450000000000004 -0.1085 0.011666666666666667 -0.009333333333333334 0.004666666666666667 -0.012833333333333334 0.019833333333333335 -0.5016666666666667 -0.0035000000000000005 -0.014000000000000002 -0.011666666666666667 -0.011666666666666667 -0.21466666666666667 -0.10733333333333334Z" strokeWidth="1.1667" />
                <path fill="currentColor" d="M14 2.916666666666667A1.75 1.75 0 0 1 15.750000000000002 4.666666666666667v6.302333333333334L21.207666666666668 7.816666666666667a1.75 1.75 0 0 1 1.75 3.031L17.5 14l5.457666666666667 3.151166666666667a1.75 1.75 0 0 1 -1.75 3.031l-5.457666666666667 -3.1500000000000004V23.333333333333336a1.75 1.75 0 0 1 -3.5 0v-6.302333333333334L6.792333333333334 20.183333333333337a1.75 1.75 0 1 1 -1.75 -3.031L10.5 14 5.042333333333334 10.848833333333333a1.75 1.75 0 0 1 1.75 -3.031l5.457666666666667 3.1500000000000004V4.666666666666667A1.75 1.75 0 0 1 14 2.916666666666667Z" strokeWidth="1.1667" />
              </g>
            </svg>
            <p className="cta-callout-title min-w-0 font-semibold text-zinc-800 dark:text-zinc-200">
              {title}
            </p>
          </div> : null}
        <div className={`cta-callout-body text-sm leading-normal text-zinc-800 dark:text-zinc-200${title ? " cta-callout-body--indented" : ""}`}>
          {children}
        </div>
        <a href={href} target={target} rel={linkRel} onClick={handleCtaClick} data-docs-cta={trackingEvent || undefined} className="cta-callout-button inline-flex items-center justify-center gap-1 rounded-full bg-white px-3 py-1.5 text-sm font-semibold transition hover:bg-zinc-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/50 dark:bg-white dark:hover:bg-zinc-200">
          {buttonLabel}
          <span className="cta-callout-button-arrow" aria-hidden="true">
            →
          </span>
        </a>
      </div>
    </section>;
};

Saved and scheduled queries help you answer questions like:

* How much volume moved in each currency last week?
* How many transactions are still `INFLIGHT`?
* Which identities gained new wallets this month?

The **Queries** tab is where you find work you have already done: **Saved**, **Scheduled**, and **History**.

***

## Write a query

Click `New query` to open a blank editor. Insights runs SQL against a **data lake**: a stored copy of your ledgers, balances, transactions, and identities built for reports. If you want a starter, open a [template](/cloud/insights/templates) and change the filters for the report you need.

### Write your first SELECT

SQL is how you ask Insights for rows from a table. Build a query in this order. Each step adds one part of the same report: how many applied transactions there are for each currency and status.

<Steps>
  <Step title="SELECT the columns">
    Use **`SELECT`** to choose which columns to show, or to count rows or add up a total.

    ```sql theme={"system"}
    SELECT
      currency,
      status,
      COUNT(*) AS txn_count
    ```

    This chooses the columns to return: currency, status, and a count of rows named `txn_count`.
  </Step>

  <Step title="FROM a table">
    Use **`FROM`** to pick the table to read: `transactions`, `balances`, `ledgers`, or `identity`. Open [Schema](/cloud/insights/schema) to see what each table stores and which columns you can select.

    ```sql theme={"system"}
    SELECT
      currency,
      status,
      COUNT(*) AS txn_count
    FROM transactions
    ```

    This reads those columns from the `transactions` table.
  </Step>

  <Step title="WHERE to filter rows">
    Use **`WHERE`** when you only want some rows. For example, `status = 'APPLIED'` keeps applied transactions and drops the rest.

    ```sql theme={"system"}
    SELECT
      currency,
      status,
      COUNT(*) AS txn_count
    FROM transactions
    WHERE status = 'APPLIED'
    ```

    This keeps only applied transactions and drops every other status.
  </Step>

  <Step title="GROUP BY to summarize">
    Use **`GROUP BY`** when you want one result row per group, for example one row per currency and status.

    ```sql theme={"system"}
    SELECT
      currency,
      status,
      COUNT(*) AS txn_count
    FROM transactions
    WHERE status = 'APPLIED'
    GROUP BY currency, status
    ```

    This makes one result row for each currency and status pair, with the count for that pair.
  </Step>

  <Step title="ORDER BY to sort">
    Use **`ORDER BY`** when you want the results sorted.

    ```sql theme={"system"}
    SELECT
      currency,
      status,
      COUNT(*) AS txn_count
    FROM transactions
    WHERE status = 'APPLIED'
    GROUP BY currency, status
    ORDER BY txn_count DESC
    ```

    This sorts the rows so the highest counts appear first.
  </Step>

  <Step title="LIMIT the rows">
    Use **`LIMIT`** to cap how many rows come back. Use `1000` for listings, and stay at or under `5000`.

    ```sql theme={"system"}
    SELECT
      currency,
      status,
      COUNT(*) AS txn_count
    FROM transactions
    WHERE status = 'APPLIED'
    GROUP BY currency, status
    ORDER BY txn_count DESC
    LIMIT 1000
    ```

    This returns at most 1000 rows so the workbench stays responsive.
  </Step>
</Steps>

That full query only counts rows. It does not add up money. To add up money, see [Sum amounts correctly](/cloud/insights/queries#sum-amounts-correctly). To look at one currency only, add `AND currency = 'USD'` to the `WHERE` line.

Set the toolbar date range for the period you care about before you run. See [How the lake works](/cloud/insights/lake).

### Use TRY\_CAST on dates and amounts

When you query across many days of history, wrap dates and amounts in `TRY_CAST` so a type mismatch does not fail the whole query.

```sql theme={"system"}
TRY_CAST(created_at AS TIMESTAMP)
TRY_CAST(precise_amount AS DECIMAL(38, 8))
  / NULLIF(TRY_CAST(precision AS DECIMAL(38, 8)), 0)
```

`TRY_CAST` returns `NULL` when a value cannot be converted to that type, instead of failing the whole query. `NULLIF(..., 0)` stops a divide by zero.

### Sum amounts correctly

When you want to add up money, not just count rows, divide `precise_amount` by `precision` for each row, then add those results together. Every transaction stores that value three ways. See [Precision](/transactions/precision) for how Core stores these fields.

* **`precise_amount`:** the integer in the currency's smallest unit (cents, satoshi).
* **`precision`:** how many of those units make one whole unit (100 for USD, 100000000 for BTC).
* **`amount`:** the human-readable value Blnk derives.

| What you posted | `precise_amount` | `precision` | `amount`    |
| :-------------- | :--------------- | :---------- | :---------- |
| USD 7.25        | `725`            | `100`       | **7.25**    |
| BTC 0.00041     | `41000`          | `100000000` | **0.00041** |

Follow these steps to total `APPLIED` transaction amounts by currency.

<Steps>
  <Step title="Divide each row into a money value">
    Use `precise_amount / precision` (with `TRY_CAST`) so each row becomes a real amount like `7.25`, not `725`. Use this instead of `SUM(amount)` when your date range covers many days of history. Do not sum `precise_amount` on its own: cents and satoshis are different units.

    ```sql theme={"system"}
    TRY_CAST(precise_amount AS DECIMAL(38, 8))
    / NULLIF(TRY_CAST(precision AS DECIMAL(38, 8)), 0)
    ```

    `DECIMAL(38, 8)` means up to 38 digits in total, with up to 8 after the decimal point. That gives the division enough room to stay accurate. `NULLIF(..., 0)` stops a divide by zero.
  </Step>

  <Step title="Add those values with SUM">
    Wrap that expression in `SUM(...)` and name it `volume`.

    ```sql theme={"system"}
    SUM(
      TRY_CAST(precise_amount AS DECIMAL(38, 8))
      / NULLIF(TRY_CAST(precision AS DECIMAL(38, 8)), 0)
    ) AS volume
    ```

    This adds the money values across rows and returns one total called `volume`.
  </Step>

  <Step title="Group by currency">
    A total only makes sense inside one currency. Add `currency` to `SELECT` and `GROUP BY currency`, or filter to one currency in `WHERE` first.
  </Step>

  <Step title="Put it together">
    Full query for `APPLIED` transaction amounts by currency:

    ```sql theme={"system"}
    SELECT
      currency,
      SUM(
        TRY_CAST(precise_amount AS DECIMAL(38, 8))
        / NULLIF(TRY_CAST(precision AS DECIMAL(38, 8)), 0)
      ) AS volume
    FROM transactions
    WHERE status = 'APPLIED'
    GROUP BY currency
    ORDER BY volume DESC
    LIMIT 1000
    ```

    This returns one row per currency with the total `APPLIED` amount for that currency.
  </Step>
</Steps>

***

## Save a query

After you run a query you want to keep, click **Save**. Until you save, the draft is named Untitled lake query.

Saved queries live on this instance. Open one from **Saved** to put that SQL back in the editor.

<img src="https://mintcdn.com/blnk/ghk1U3dxdtZ8Bx1O/cloud/img/insights/run-and-save.gif?s=f9b63e2a0fc6e72e890fb5e80dba4919" alt="Set the date range, run a query, then save it from Actions" className="rounded-lg" width="1716" height="1080" data-path="cloud/img/insights/run-and-save.gif" />

## Schedule a query

**Schedule query** appears in **Actions** once the query is saved with no unsaved edits.

1. Open **Actions** → **Schedule query**.
2. Pick a **Preset** (Hourly, Daily at 09:00, Weekly on Monday, or Monthly on the 1st). The **Cron expression** field fills from the preset. You can leave it as is unless you need a custom schedule.
3. Confirm **Timezone**, then click **Save schedule**.

Scheduled runs use the saved query and keep a result preview in run history. They do not send external notifications.

* Schedules always run the **last saved version**. Editor changes do not run until you save again.
* A scheduled report is the same SQL you run by hand. If a run returns empty, check the date range first.

The query appears under **Scheduled**.

<img src="https://mintcdn.com/blnk/ghk1U3dxdtZ8Bx1O/cloud/img/insights/schedule-query.gif?s=5af56e9a5cb7a8fc20c610d2d1b4987b" alt="Open Actions, choose Schedule query, pick a preset and timezone, then save the schedule" className="rounded-lg" width="1536" height="1080" data-path="cloud/img/insights/schedule-query.gif" />

## Reopen a past run

**History** is past runs from this instance. Open a row to reopen a query you already executed.

History is not the same list as **Saved**. Saved stores the query. History stores the run.

<img src="https://mintcdn.com/blnk/ghk1U3dxdtZ8Bx1O/cloud/img/insights/queries-history.gif?s=f3cc1447a961310fca6a64afeadc376e" alt="Open History on the Queries tab to reopen a past run" className="rounded-lg" width="1536" height="1080" data-path="cloud/img/insights/queries-history.gif" />

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="My totals do not match the dashboard">
    Check two things first. You summed `precise_amount` without dividing by `precision`, or you summed across currencies. See [Sum amounts correctly](/cloud/insights/queries#sum-amounts-correctly). Do not sum `balances.balance` across days for a live portfolio total. That column is a per-day copy, and current holdings belong on the [Data API](/cloud/reference/data-api).
  </Accordion>

  <Accordion title="Transaction counts look inflated after a join">
    `balances`, `ledgers`, and `identity` can repeat across days in your date range. Collapse each entity to one row per ID before you join, as in [How to join them](/cloud/insights/schema#how-to-join-them). Do not order by `created_at` to pick a latest wallet snapshot.
  </Accordion>

  <Accordion title="Open inflight holds look wrong">
    Set a date range wide enough to include both the parent hold and its commit or void days. Then use the parent-child template in [Open inflight holds](/cloud/insights/templates#open-inflight-holds). A bare `WHERE status = 'INFLIGHT'` includes resolved parents.
  </Accordion>

  <Accordion title="Query returns no rows">
    Check the toolbar date range first. It selects which days of history are scanned. An empty range means an empty result, even if your `WHERE` clause looks right.
  </Accordion>

  <Accordion title="Query fails with a type error">
    A column is typed differently across days of history. Wrap comparisons and aggregates in `TRY_CAST` as shown in [Use TRY\_CAST on dates and amounts](/cloud/insights/queries#use-trycast-on-dates-and-amounts).
  </Accordion>

  <Accordion title="Query rejected: statement not allowed">
    Use a `SELECT` or `WITH` statement. Post new ledger records with the [Ledger API](/reference/create-transaction).
  </Accordion>

  <Accordion title="Query is slow or times out">
    Shrink the toolbar date range, filter to fewer currencies, select named columns instead of `*`, and keep a `LIMIT`.
  </Accordion>
</AccordionGroup>

***

## 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).

<CtaCallout title="Need help with your product?" href="https://blnkfinance.com/contact/us?utm_source=blnk_docs&utm_medium=documentation&utm_campaign=home%2Finstall" buttonLabel="Speak with us" trackingEvent="clicked_pro_support">
  Get dedicated support for architecture reviews, integration planning, ledger workflows, and production deployment.
</CtaCallout>

<RelatedTopics
  items={[
{ title: "Insights overview", href: "/cloud/insights/overview" },
{ title: "How the lake works", href: "/cloud/insights/lake" },
{ title: "Schema", href: "/cloud/insights/schema" },
{ title: "Templates", href: "/cloud/insights/templates" },
]}
/>
