@get-asset/sdk
AssetProvider
Wraps your app, configures the API client (base URL + access token), and stores the shared reporting range (dateFrom/dateTo) that every date-driven component reports on, plus the accounting basis the statement components report on. Required for any @get-asset/sdk or @get-asset/react compound to fetch.
import { AssetProvider } from "@get-asset/sdk";
Props
| Prop | Type | Default | Description |
|---|---|---|---|
baseUrl | string | https://api.getasset.com | Origin of the Asset API. Set once on the client; every subsequent fetch uses it. Override only when pointing at a staging or self-hosted instance. |
businessId | string | — | Business whose financials you're displaying. Forwarded as the business_id path parameter on every request. Optional — omit it during flows that create a business (e.g. onboarding) and set it later with setBusinessId. |
accessTokenrequired | string | (businessId?: string) => Promise<string> | — | Either a literal token string or an async function that returns one. The function form receives the current businessId (undefined when none is set) so it can mint a token scoped to that business. The provider keeps the latest token in a ref so every in-flight request picks up the current value. |
timeZone | string | runtime IANA time zone | IANA time zone used to resolve user-local picker dates such as today. Pass it explicitly during SSR for deterministic output. |
defaultDateFrom | CalendarDateString | January 1 of the current year | Initial inclusive start of the shared reporting range, in YYYY-MM-DD form. Must be supplied together with defaultDateTo — one alone throws. See Reporting range. |
defaultDateTo | CalendarDateString | December 31 of the current year | Initial inclusive end of the shared reporting range, in YYYY-MM-DD form. Must be supplied together with defaultDateFrom, and must not fall before it. |
dateFrom | CalendarDateString | — | Controlled inclusive start of the shared reporting range. Must be supplied together with dateTo — one alone throws. When set the provider mirrors these props instead of owning the range; see Controlling the range. |
dateTo | CalendarDateString | — | Controlled inclusive end of the shared reporting range. Must be supplied together with dateFrom, and must not fall before it. |
onDateRangeChange | (range: { start, end }) => void | — | Called with the inclusive range a picker selected, whenever it differs from the current range. Controlled consumers adopt it into their own state to move the range; uncontrolled consumers can use it to observe changes (e.g. to mirror the selection into a URL). |
defaultBasis | "accrual" | "modified_cash" | resolved from the Business's country | Initial value for the shared basisstate that every statement component reports on. Omit and each component resolves it from the Business's country instead — which costs a request, so set it when you already know the basis. See Accounting basis. |
childrenrequired | ReactNode | — | Your app tree. |
Access token
Pass either a literal token (e.g. when you've already exchanged one server-side and rendered it into the page) or a function that fetches one. The function form is re-invoked when accessToken changes, so you can rotate tokens without remounting the provider.
Static string — fine when the token is short-lived but already on the client:
<AssetProvider businessId={businessId} accessToken={accessToken}>{children}</AssetProvider>
Async function — usually a fetch to a route that exchanges your client_id/client_secret server-side. Never put the secret in the browser.
// Module scope (or useCallback) — a stable identity, so re-renders don't re-mint.const fetchToken = (businessId?: string) =>fetch("/api/asset/token").then((r) => r.json()).then((j) => j.token);<AssetProvider businessId={businessId} accessToken={fetchToken}>{children}</AssetProvider>
Reporting range
The provider holds one complete, ordered, inclusive dateFrom/dateTo pair, and every date-driven component reports on it. Nothing else is stored: a month, a quarter and a year are just ranges, so a card never has to reconcile separate fields.
<AssetProviderbusinessId={businessId}accessToken={fetchToken}defaultDateFrom="2026-04-01"defaultDateTo="2026-04-30">{/* Every date-driven component reports on April 2026. */}{children}</AssetProvider>
Each component resolves its own window, first match winning:
- The component's own
dateFrom/dateToprops → use them. They are atomic: pass both or neither, and passing one alone throws. - Props omitted → the provider's shared range.
- No
defaultDateFrom/defaultDateToon the provider either → the current calendar year, resolved in the provider'stimeZone.
Cards that compare against a previous period mirror whatever range they resolved: a calendar period is compared against the previous one of the same kind (February against January, Q1 against Q4 of the prior year, a year against the year before), and any other range against the same number of days immediately before it.
Driving the range
DatePicker is the control for it — its period and range panels are two views of the same pair, and a valid choice in either updates both values immediately. Every picker instance and every component under the provider moves together, with no key or per-instance value prop.
<AssetProvider defaultDateFrom="2026-04-01" defaultDateTo="2026-04-30" {/* … */}><AssetDatePicker /> {/* moves the range for every component below */}<AssetSnapshotCard /> {/* April 2026, compared against March 2026 */}<AssetRevenueCard /> {/* follows the same range */}<AssetExpensesCard dateFrom="2026-01-01" dateTo="2026-12-31" />{/* detached: all of 2026, and the picker no longer moves it */}</AssetProvider>
Controlling the range
By default the provider owns the range and the picker mutates it in place. Pass dateFrom/dateTo (with onDateRangeChange) to own it yourself instead — the provider then mirrors your props, and picker selections only call the callback until you re-render with the new values. That makes app state (URL search params, a store) the single source of truth: seed it once, adopt selections in the callback, and every screen shares one range without any per-screen sync code. defaultDateFrom/defaultDateTo are ignored while controlled.
const [range, setRange] = useRangeSearchParams(); // your state — URL params, a store, …<AssetProviderdateFrom={range.start}dateTo={range.end}onDateRangeChange={setRange}{/* … */}><DatePicker.Root>{/* … */}</DatePicker.Root><SnapshotCard.Root>{/* … */}</SnapshotCard.Root></AssetProvider>
Pinning one component
Passing dateFrom/dateTo to a component detaches it from the shared range entirely, so the picker no longer moves it. Use periodDateRange to get the calendar bounds of a month, quarter or year without spelling the dates out.
import { periodDateRange } from "@get-asset/sdk";const q2 = periodDateRange({ type: "quarter", year: 2026, quarter: 2 });<AssetRevenueCard dateFrom={q2.start} dateTo={q2.end} />;
Accounting basis
The statement components (RevenueCard, ExpensesCard, SnapshotCard, OperatingExpensesCard, ProfitLossCard, ProfitLossReport, and BalanceSheetReport) report on either the Accrual basis (accrual on the wire) or the Cash basis (modified_cash). Cashflow components take no basis — cashflow is a cash concept. Each component resolves its own basis, first match winning:
- The component's
basisprop set → use it. - Prop omitted → fall back to the provider's
basis(defaultBasis, or whateversetBasiswas last called with). - Provider basis unset → look the Business up and use its country default: Canadian Businesses report on Accrual, everyone else on Cash.
Only the last step costs a request, and components hold their own fetch until it answers, so it lands in front of every report on the page. TanStack shares the one request across them, but defaultBasisskips it altogether — set it whenever you already know which basis the viewer should see. A Business lookup that fails falls back to Cash and stays there for the life of the component: a component's retry() re-runs its report query, not the basis lookup.
<AssetProvider businessId={businessId} accessToken={accessToken} defaultBasis="accrual"><AssetProfitLossReport basisToggle /> {/* toggle drives every card below */}<AssetRevenueCard /> {/* follows the provider — accrual */}<AssetRevenueCard basis="modified_cash" /> {/* pinned to cash, ignores the toggle */}</AssetProvider>
Because the basis lives here rather than on each component, the opt-in basisToggle control can never leave a screen half cash and half accrual — flipping it on one component calls setBasis and moves every component that inherits from the provider. Components pinned with their own basis prop are the exception: they ignore the toggle, which is how you deliberately put both bases on one screen.
useAsset
The same context is readable from any child, so your own chrome can label or lay out around the range every card is reporting on. Moving the range is the picker's job — mount DatePicker (or AssetDatePicker) rather than writing to the context; every consuming card re-fetches automatically when it commits.
import { useAsset } from "@get-asset/sdk";function RangeCaption() {const { dateFrom, dateTo } = useAsset();return (<p>Reporting on {dateFrom} to {dateTo}</p>);}
Returned config
useAsset() returns the same object the provider stores internally:
| Prop | Type | Default | Description |
|---|---|---|---|
baseUrl | string | — | API origin (echo of the prop). |
businessId | string | — | Active business id, or "" when none is set. |
token | string | — | The currently-resolved access token. |
isAuthenticating | boolean | — | True while the token is still being (re-)provisioned for the current businessId — e.g. right after setBusinessId. Gate business-scoped actions on it so a request never goes out under a stale token. |
timeZone | string | — | Resolved IANA time zone used by calendar-date controls. |
dateFrom | CalendarDateString | — | Inclusive start of the shared reporting range. See Reporting range. |
dateTo | CalendarDateString | — | Inclusive end of the shared reporting range. |
basis | "accrual" | "modified_cash" | null | — | Current shared accounting basis, or nullwhen none has been set — each statement component then resolves one from the Business's country. See Accounting basis. |
setBasis | (basis: "accrual" | "modified_cash") => void | — | Switch the accounting basis for every statement component that inherits it. Backs the built-in basisToggle control, and there is no null — once a basis is set, country resolution is out of the picture. |
setBusinessId | (businessId: string | null) => void | — | Point the provider at a business. Changing it re-invokes the accessToken function with the new id to re-provision the token — used by flows like onboarding that create a business and then switch from a creation-scoped token to a business-scoped one. |
useAsset() throws. Wrap your app in AssetProvider first.