Sign in →
Embed Widgets1 min read

AforoInvoiceList

Paginated customer invoice table with status filter, search, PDF download, and Pay Now action. The first data-dense, customer-scoped widget.

Updated 2026-06-15Suggest edits
Docs Embed Widgets InvoiceList
Live preview — synthetic data
AforoInvoiceList
Paginated invoice table with PDF download + Pay Now hooks.
Loads the widget from https://embed-demo.aforo.ai with synthetic data.

What it renders#

A paginated table of the authenticated customer's invoices, with:

  • 5-state filter chip row: All / Unpaid / Paid / Overdue / Void
  • 300ms-debounced search by invoice number
  • Per-row expand chevron revealing line-item count + period dates + actions
  • Download PDF button (opens in new tab with noopener,noreferrer)
  • Pay Now button (currently routes through the Phase 0 stub; Prompt 7 CheckoutFlow replaces it with real embedded checkout)
  • Pagination footer with Previous/Next + page indicator
  • In-widget 30s client cache — filter scrubbing within the cache window doesn't re-fetch
From Operator Portal → Settings.
From Operator Portal → Embed Studio → Keys.
Minted by your backend per customer session — see Authentication.
Script tag
<div data-aforo-widget="invoice-list"
     data-aforo-tenant-slug="your-tenant"
     data-aforo-embed-key="ek_live_replace_me"
     data-aforo-bridge-token="bridge.replace.me"></div>

<script src="https://cdn.aforo.ai/embed/loader.js" async></script>
NPM / React
import { AforoInvoiceList } from '@aforoai/storefront-widgets';

export function Pricing() {
  return (
    <AforoInvoiceList
      tenantSlug="your-tenant"
      embedKey="ek_live_replace_me"
  bridgeToken="bridge.replace.me"
    />
  );
}
Preview with my account
Fill in Tenant slug above to enable this preview.

Mount examples#

app/account/invoices/page.tsx
import { AforoInvoiceList } from '@aforoai/storefront-widgets';

export default async function InvoicesPage() {
  const session = await getSession();
  const bridgeToken = await mintBridgeToken({
    externalId: session.customerId,
    email: session.email,
  });

  return (
    <AforoInvoiceList
      tenantSlug="acme"
      embedKey="embk_live_…"
      bridgeToken={bridgeToken}
      defaultStatus="unpaid"
      pageSize={25}
    />
  );
}

Props reference#

PropTypeDefaultDescription
tenantSlugstring(required)Your tenant slug
embedKeystring(required)Embed key from Embed Studio
bridgeTokenstring?Required unless magicLinkMode is true
magicLinkModeboolean?falseUse Aforo email magic links instead of bridge token
defaultStatus"all" | "unpaid" | "paid" | "overdue" | "void""all"Initial filter chip
pageSizenumber?25Rows per page (max 100)
theme"auto" | "light" | "dark""auto"Color scheme
themeOverridesPartial<ThemeTokens>?Per-widget theme tuning
localestring?navigator.languageBCP-47 locale for currency + date formatting

Real-time updates#

InvoiceList opens an SSE connection on mount and listens for two server-pushed events:

  • aforo.invoice.paid — busts the 30s client cache and refetches the current page so the row's status flips from UNPAID to PAID without manual refresh.
  • aforo.invoice.created — busts the cache. Refetches if the customer is currently on page 0 (where the new invoice would appear). Other pages get their cache invalidated for next navigation.
PRO TIP
SSE connections share across multiple mounted widgets on the same page. If the customer also has a UsageMeter open (Phase 1), both widgets reuse the same connection. When the last widget unmounts, the connection stays open for 30 seconds in case a new widget mounts — preventing a disconnect/reconnect cycle on page transitions.

If your portal doesn't have its own session, set magicLinkMode={true}. The widget renders an email input. After the customer submits, they see a "Check your inbox" confirmation. The link in the email brings them back with a session JWT.

account.tsx
<AforoInvoiceList
  tenantSlug="acme"
  embedKey="embk_live_…"
  magicLinkMode={true}
/>

Full details — rate limits, anti-enumeration posture, customization — in the .

Events#

InvoiceList emits 8 events; see the for full payload shapes. Notable privacy guarantee: aforo.invoice-list.search_changed contains only queryLength, never the actual search query — so screen contents are not leaked to the parent page.

Limitations#

  • Pay Now uses Phase 0 stub. Today, clicking Pay Now surfaces an inline "Coming soon" notice. Prompt 7 (CheckoutFlow) wired the real cart-create path, and the InvoiceList Pay Now path drops in the new flow without code changes on your side.
  • Sort is fixed. Server-side sort is hardcoded to issuedAt DESC. Sortable column headers are Phase 1.
  • Inline line-item detail. v0.1.x shows only the line-item count in the expanded row. The full breakdown is in the PDF. A future minor adds an inline line-item table.
  • Pre-signed PDF URLs. v0.1.x uses session-authenticated companion endpoints for PDF downloads — equivalent UX, but you can't share a download link via email. Pre-signed URLs are filed as a follow-up.