---
title: "Portfolio statistics, cashflow and what the AI did"
method: GET
path: "/api/v1/numbers"
tags: ["Portfolio"]
---

# Portfolio statistics, cashflow and what the AI did

`GET /api/v1/numbers`

## Request

- Base URL: `http://localhost:5180`
- URL: `http://localhost:5180/api/v1/numbers`
- Auth: API key in cookie `better-auth.session_token`

Every money figure is integer cents. Cashflow is money actually received and actually spent, by month. `digest.hoursSaved` is deprecated: no record backs the estimate, nothing on screen reads it, and v2 drops it.

## Response `200`

The dashboard figures.

- object
  - `ok` unknown, required
  - `data` object, required
    - `stats` object, required
      - `buildings` number, required
      - `units` number, required
      - `lettableUnits` number, required
      - `occupiedUnits` number, required
      - `vacantUnits` number, required
      - `shortStayUnits` number, required
      - `occupancyRate` number, required: 0–1.
      - `monthlyRentRoll` integer, required: Contracted monthly rent across active long-term leases.
      - `monthlyIncomeEstimate` integer, required: Rent roll plus a trailing-12-month monthly average of short-stay net revenue.
      - `arrearsTotal` integer, required: Money, always in integer cents. €1,250.00 is 125000.
      - `arrearsCases` number, required
      - `portfolioValue` integer, required: Money, always in integer cents. €1,250.00 is 125000.
      - `mortgageOutstanding` integer, required: Money, always in integer cents. €1,250.00 is 125000.
      - `equity` integer, required: Money, always in integer cents. €1,250.00 is 125000.
      - `ltv` number, required: Mortgage outstanding ÷ portfolio value, 0–1.
      - `grossYield` number, required: Annualised: (rent roll × 12) ÷ portfolio value.
    - `collection` object, required: This month’s collection status. The same figures /payments returns.
      - `expected` integer, required: Money, always in integer cents. €1,250.00 is 125000.
      - `collected` integer, required: Money, always in integer cents. €1,250.00 is 125000.
      - `outstanding` integer, required: Money, always in integer cents. €1,250.00 is 125000.
      - `rate` number, nullable, required: collected ÷ expected, 0–1. Null when nothing was due, because a share of nothing is not a figure.
      - `invoiceCount` number, required
      - `paidCount` number, required
      - `overdueCount` number, required: Notices whose grace days are over and whose bank has reported past the due date. One not seen yet is never counted.
      - `allInBank` boolean, required: True when every payment behind what was collected came off a bank movement. False when nothing was collected.
    - `cashflow` object, required
      - `labels` string[], required
      - `income` integer[], required
      - `expenses` integer[], required
      - `net` integer[], required
    - `mix` object[], required: Income split across long-term rent, short stay and charge provisions, as one month of each.
      - `key` string, required: `long_term`, `short_stay` or `charges`.
      - `label` string, required: The words an owner would use for the source, ready to put on a legend.
      - `value` integer, required: One month from this source. Short stay is the trailing 12 months of net payouts divided by 12.
    - `digest` object, required
      - `today` number, required
      - `last7d` number, required
      - `hoursSaved` number, required: Estimated owner-hours saved, summed across every workflow. Deprecated: no record backs the estimate and v2 drops it. Nothing on screen reads it.
      - `recent` object[], required: The last dozen things the AI did, newest first. Future-dated steps of a scheduled run are not counted as done.
        - `id` string, required: Stable record id, e.g. `lse_012`.
        - `at` string, required: ISO 8601 timestamp.
        - `action` string, required: `lease.indexed`, `message.sent`, `approval.rejected`…
        - `summary` string, required: Human-readable. Every action the AI takes leaves one.
        - `entity` object, required
          - `kind` string, required: Which kind of record this points at, for example `lease`, `unit` or `invoice`.
          - `id` string, required: Stable record id, e.g. `lse_012`.
          - `label` string, required: How the record reads to a person, so a trail can be followed without a second lookup.
        - `workflowRunId` string: Stable record id, e.g. `lse_012`.
        - `severity` string, required
      - `runs` object[], required: The eight most recent workflow runs, newest first, each with the steps it took and why any step was skipped.
        - `id` string, required: Stable record id, e.g. `lse_012`.
        - `workflowId` string, required: Stable record id, e.g. `lse_012`.
        - `startedAt` string, required: ISO 8601 timestamp.
        - `finishedAt` string: ISO 8601 timestamp.
        - `status` 'running' | 'awaiting_approval' | 'completed' | 'failed' | 'skipped' | 'cancelled', required
        - `subject` object, required: The record the run was about, which is what triggered it.
          - `kind` string, required: Which kind of record this points at, for example `lease`, `unit` or `invoice`.
          - `id` string, required: Stable record id, e.g. `lse_012`.
          - `label` string, required: How the record reads to a person, so a trail can be followed without a second lookup.
        - `triggerSummary` string, required
        - `steps` object[], required
          - `actionId` string, required: Stable record id, e.g. `lse_012`.
          - `kind` string, required
          - `label` string, required
          - `status` 'pending' | 'awaiting_approval' | 'done' | 'skipped' | 'failed', required
          - `at` string: ISO 8601 timestamp.
          - `produced` object[]: What this step created, so the trail stays navigable.
            - `kind` string, required: Which kind of record this points at, for example `lease`, `unit` or `invoice`.
            - `id` string, required: Stable record id, e.g. `lse_012`.
            - `label` string, required: How the record reads to a person, so a trail can be followed without a second lookup.
          - `detail` string
          - `skipReason` string: Why a step was skipped. Saying what stopped and why is the whole trust proposition.
        - `approvalRequestId` string: Stable record id, e.g. `lse_012`.
        - `error` string
        - `summary` string, required: One owner-readable line for the activity feed.
      - `headline` string, required: One line, in Dardaris’s own voice.
  - `meta` object, required
    - `requestId` string, required: Echoed in the x-request-id header. Quote it in a bug report.

---

[API](https://skmtc.dev/dardaris/apis/dardaris-api.md) · [All operations](https://skmtc.dev/dardaris/apis/dardaris-api/llms.txt) · [OpenAPI document](https://skmtc.dev/dardaris/apis/dardaris-api/revisions/4aff33835c73?raw)
