Expense Management

A complete expenses surface: My and Team transactions tabs with Action Items, a searchable transactions table, receipt upload, and a full-screen transaction review dialog.

Part ofFinOps

A complete expenses surface: My and Team transactions tabs with Action Items, a searchable transactions table, receipt upload, and a full-screen transaction review dialog.


Quick Start

import { ExpenseManagementWidget } from '@tesouro/embedded-components-react';

export function ExpensesPage() {
  return (
    <ExpenseManagementWidget
      baseUrl="https://api.example.com"
      widgetToken="wt_live_abc123"
      organizationId="org_xyz"
      bankDisplayName="Example Bank"
    />
  );
}

The widget renders no page heading; your page supplies it.

Behaviour

  • The Team tab appears when the token's scopes include org-wide expense read access, or when the signed-in user manages at least one direct report (their Team rows are scoped to those reports). When Team is available the widget opens on it.
  • The settings entry point renders only when you pass onSettingsClick and the user's scopes allow it. The widget never navigates itself.
  • Receipt upload uses the widget's own upload and mailbox address by default. If you supply receiptUpload.onFileUpload, the built-in receipt-matching tracker doesn't run.

Props

Widget props

PropTypeDefaultDescription
bankDisplayNamestringundefinedBank name shown in the attribution header.
bankLogoSrcstringundefinedURL for the bank logo in the attribution header.
bankLogoAltstringundefinedAlt text for the bank logo.
isBankingTaglineVisiblebooleanundefinedShows the "Business Banking provided by" tagline beside the header.
onSettingsClickEvent() => voidundefinedRenders a scope-gated settings entry point in the header; navigate from this callback.
receiptUploadobjectundefinedOverrides the receipt-upload control's props, such as onFileUpload / onAwaitMatching; see Receipt upload.
Label overrides
PropTypeDefaultDescription
screenLabelsLabelsPartial<ExpensesScreenLabels>EnglishTab labels and other screen shell copy.
headerLabelsLabelsPartial<ExpensesHeaderLabels>EnglishBank-attribution header copy.
transactionsTableLabelsLabels
Partial<ExpensesTransactionsTableLabels>
Partial<ExpensesTransactionsTableLabels>
EnglishTransactions table column headers and empty/loading copy.
filterBarLabelsLabels
Partial<ExpensesTransactionsFilterBarLabels>
Partial<ExpensesTransactionsFilterBarLabels>
EnglishSearch/filter bar copy.
actionItemsLabelsLabelsPartial<ActionItemsListLabels>EnglishAction Items list copy.
statusBadgeLabelsLabelsPartial<ExpenseStatusBadgeLabels>EnglishStatus pill copy, shared between the table and the review dialog.
transactionDetailsLabelsLabelsPartial<TransactionDetailsLabels>EnglishReview dialog copy.
transactionDetailsCardLabelsLabelsPartial<AccordionItemCardLabels>EnglishCopy for the accordion cards inside the review dialog.
transactionDetailsActionsLabelsLabels
Partial<TransactionDetailsActionsLabels>
Partial<TransactionDetailsActionsLabels>
EnglishCopy for the review dialog's action buttons.
fileViewerLabelsLabelsPartial<FileViewerLabels>EnglishCopy for the receipt/attachment file viewer inside the review dialog.

Examples

Bank attribution, settings entry point and custom upload

<ExpenseManagementWidget
  bankDisplayName="Example Bank"
  bankLogoSrc="/assets/bank-logo.svg"
  isBankingTaglineVisible
  onSettingsClick={() => router.push('/settings/expenses')}
  receiptUpload={{
    onFileUpload: (file) => myUploadHandler(file),
  }}
/>

Receipt upload

The "Upload receipts" button in the header opens a popover with a drag-and-drop file picker and a copyable forwarding email address. receiptUpload overrides its props; every field is optional and falls back to the widget's own upload, matching tracker and mailbox address.

FieldTypeDescription
emailAddressstringForwarding email address shown with a copy button.
onFileUpload(file: File) => unknown | Promise<unknown>Called once per selected file and awaited; a rejection counts as a failed upload.
onAwaitMatching(uploadedCount: number) => Promise<ReceiptMatchSummary>Awaited after every file in a batch uploads. Resolve once matching finishes.
isUploadingbooleanHost loading flag. While true (or while a batch runs) the popover shows a processing state and blocks new uploads.
  • onFileUpload is called once per file, up to 10 files per batch. The widget shows a loading toast, then a success, partial or failure toast based on how many calls resolved.
  • With onAwaitMatching, a fully successful batch keeps its toast open through receipt matching, then reports all matched, a partial count, or "still processing". If it rejects, the toast falls back to the plain upload success message.
  • ReceiptMatchSummary is { total, matched, unmatched, unreadable, stillProcessing }, each a number count of the batch's receipts. stillProcessing counts receipts that never settled in time; it is not a failure.

TypeScript

import type {
  ExpenseManagementWidgetProps,
  TransactionDetailsLabels,
  TransactionDetailsActionsLabels,
  AccordionItemCardLabels,
  FileViewerLabels,
} from '@tesouro/embedded-components-react';

Auth fields are inherited from WidgetProviderProps; see WidgetProvider for cascade rules.

On this page