Flowcourier.Umbraco.Foundation 17.5.8

dotnet add package Flowcourier.Umbraco.Foundation --version 17.5.8
                    
NuGet\Install-Package Flowcourier.Umbraco.Foundation -Version 17.5.8
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Flowcourier.Umbraco.Foundation" Version="17.5.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Flowcourier.Umbraco.Foundation" Version="17.5.8" />
                    
Directory.Packages.props
<PackageReference Include="Flowcourier.Umbraco.Foundation" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Flowcourier.Umbraco.Foundation --version 17.5.8
                    
#r "nuget: Flowcourier.Umbraco.Foundation, 17.5.8"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Flowcourier.Umbraco.Foundation@17.5.8
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Flowcourier.Umbraco.Foundation&version=17.5.8
                    
Install as a Cake Addin
#tool nuget:?package=Flowcourier.Umbraco.Foundation&version=17.5.8
                    
Install as a Cake Tool

Flowcourier.Umbraco.Foundation

Shared base package for Flowcourier Umbraco extensions. Adds a Flowcourier group to the backoffice Settings section and gives every installed Flowcourier package a consistent settings page there — registered from C# with one line, no frontend needed in the consuming package.

How it works

A package registers its settings page in its composer:

builder.AddFlowcourierSettingsPage<AeoOptions>(
    "aeo", "AEO", icon: "icon-rocket", configSectionName: AeoOptions.SectionName);

That's it. Foundation's backoffice entry point asks the server which pages are registered and builds the Settings-section group at runtime: one menu item per package, each opening a generic page at /umbraco/section/settings/flowcourier/{packageId} that renders the package's effective settings (the values bound from appsettings), grouped and typed (booleans, strings, numbers, string lists, nested option groups).

  • No empty group — if no package registers a page, the Flowcourier group doesn't appear at all.
  • Backend-only consumers — the registering package needs no wwwroot, no umbraco-package.json, no Razor SDK; the descriptor lives in DI.
  • Read-only (for now) — the page is an effective-settings viewer. The wire contract already carries editable / source per field, so database-backed editable settings can be added later without breaking consumers (planned precedence: values set in appsettings always win and stay locked).

API

GET /umbraco/management/api/v1/fc-foundation/settings-pages — registered pages (drives the menu). GET …/settings-pages/{packageId} — the full field payload. Both require backoffice authentication with Settings-section access.

Document "Analytics" tab

Foundation owns one Analytics tab on every document and fills it with sub-tabs ("panels") contributed by whichever Flowcourier packages are installed, so an editor finds everything about the open page in one place:

Panel From
Traffic / Page speed / Search Console Flowcourier.Umbraco.Analytics.Core
SEO Flowcourier.Umbraco.AI.Agents.Seo
AI Visibility Flowcourier.Umbraco.AEO

A package contributes one by registering an ordinary extension manifest:

{
    type: "fcPageAnalyticsPanel",
    alias: "Fc.PageAnalyticsPanel.Seo",   // unique
    name: "Flowcourier SEO Page Panel",
    weight: 70,                           // higher first, left to right
    element: () => import("./seo-page-panel.element.js"),
    meta: { label: "SEO", pathname: "seo" },
    conditions: [{ alias: "Flowcourier.Condition.SeoPagePanel" }],
}
  • The tab sets contentKey, culture and isNew on the panel element, and re-sets them when the editor switches page or variant. A panel may equally consume the document workspace context itself.
  • conditions are evaluated as they are anywhere else, so a panel that needs a connected service or a configuration switch gates on it and simply isn't there otherwise.
  • meta.fallback: true marks a panel that only makes sense when nothing else does (Analytics' "how to connect a provider" guidance): it is shown only while no ordinary panel is available.
  • A lone panel is still shown as a named tab, so the editor can see what they are looking at; with none the tab itself does not appear. The chosen sub-tab is remembered for the browser session.
  • On an unsaved document the tab says so instead of showing panels — every panel reports on a page the server can resolve.

The sub-tabs are text-only and render in umb-body-layout's header slot — the same strip the document-type editor's tabs sit in, white and full-width above the padded body. A panel's own controls go on the right of that strip by exposing panelActions:

get panelActions() {
    // A Lit template, a DOM node, or an array of either — or null for none.
    return html`<fc-date-range compact .value=${this._range}
        @change=${(e) => this._onRangeChange(e)}></fc-date-range>`;
}

The tab reads the property when it renders; dispatch fc-panel-actions-change on the panel when the answer changes (a range picked, a status arriving). Actions render in the tab's shadow root, so they must carry their own styles — UUI components and Foundation's elements do, and this in a Lit event binding would be the tab, so bind handlers with an arrow.

A panel can also put a dot on its own sub-tab, for "there is something behind this tab you have not looked at". That one is declared on the manifest, because the dot has to show while another sub-tab is open and the tab only creates the element of the panel being looked at:

badge: (scope) => import("./aeo-page-badge.js").then((m) => m.pageBadge(scope)),
// scope is { contentKey, culture }; resolve to { color, title } or null.

color: "positive" paints the dot in the positive colour, anything else is muted, and title becomes the sub-tab's tooltip. It is asked once per page and variant, so keep it cheap and share whatever it fetches with the panel itself.

Charts

Foundation ships a shared charting element for Flowcourier backoffice surfaces, backed by a bundled subset of Apache ECharts (line/bar/pie — Apache-2.0, see the packed THIRD-PARTY-NOTICES.md). Consumers import one module and pass a standard ECharts option:

import "/App_Plugins/fc-foundation/fc-chart.element.js";
<fc-chart style="--fc-chart-height: 240px"
          aria-label="Hits per day"
          .option=${{ xAxis: {…}, yAxis: {…}, series: [{ type: "bar", data: […] }] }}>
</fc-chart>

The element handles the lifecycle end to end: the chart library loads lazily on first use (backoffice boot is never taxed), colors/text follow the backoffice theme (light + dark) via the UUI custom properties, resizing is automatic, and the instance is disposed when the view unmounts. Advanced escape hatches: the chart getter (raw ECharts instance) and the ready promise.

Maps

A chart can draw on a world map: set map="world" and use ECharts' geo coordinate system (geo: { map: "world" } with a scatter or map series). The element fetches and registers the outline from /App_Plugins/fc-foundation/vendor/maps/world.json the first time a page needs it. See VENDORED.md for adding other outlines.

Findings

The shared document tab opens with a strip of findings: what an editor could change on this page, with the evidence and the sub-tab that holds the detail. Every Flowcourier package contributes its own through one contract:

public sealed class MyFindingSource : IFcPageFindingSource
{
    public string SourceId => "my-package";
    public string DisplayName => "My package";
    public Task<IReadOnlyList<FcPageFinding>> GetAsync(FcPageFindingRequest request, CancellationToken ct) => …;
}
// in the composer:
builder.Services.AddTransient<IFcPageFindingSource, MyFindingSource>();

A finding is FcPageFinding(Id, SourceId, Severity, Title, Evidence, Action, Panel?, PropertyAlias?). Foundation runs all sources concurrently for the document (GET fc-foundation/page-findings?contentKey&culture), each with a timeout, sorts by severity and shows a status line for a source that failed — one slow source never blanks the strip. Sources judge a fixed window, the last Flowcourier:Findings:WindowDays (28) full days against the same length before, so findings stay stable between visits; each source caches its own result. Nothing runs on nodes page detection calls data.

Shipped sources: Analytics (traffic drop with the channel that fell, no visits, landing-page bounce and mid-funnel exit against the site median, orphaned page, site searches after the page, server time; not-indexed from the last Search Console inspection), the SEO Agent (queries just off the first page missing from title and headings) and AEO (AI bots read the page, no visitor ever arrived from an assistant).

AI-agent insights

Two contracts in Insights/ let a package read what AI crawlers and assistants did, without depending on the AEO package that records it:

Contract Scope Consumed by
IFcAeoPageInsights One page over a trailing window The SEO Agent's readiness report, the Copilot's crawler tool
IFcAeoSiteInsights The whole site over a window of UTC calendar days The Analytics section's Overview dashboard

Both live here because Foundation is the only package both AEO and its consumers reference. AEO implements and registers them; consumers resolve them optionally, so GetService<IFcAeoSiteInsights>() returning null simply means AEO is not installed and the surface shows less:

if (_services.GetService(typeof(IFcAeoSiteInsights)) is IFcAeoSiteInsights agents)
{
    var insight = await agents.GetAsync(new FcAeoSiteInsightRequest(from, to), ct);
}

Two rules the site contract states and implementations must honour: its window is closed-open over UTC calendar days (so its totals will not match a trailing days=N window for the same nominal length), and its intent split excludes spoofed fetches and sums to the headline — which has to be guaranteed by construction, because AEO's stored intent is not spoof-disjoint.

Page detection

The document Analytics tab only makes sense on web pages, and Umbraco has no flag for that: the published cache routes settings and data nodes exactly like pages, and templates prove nothing on a headless site. Foundation decides per document and culture (GET fc-foundation/page-scope), strongest signal first:

  1. Configuration under Flowcourier:Pages — IncludeDocumentTypes (when set, everything else is data), ExcludeDocumentTypes, ExcludeRoutePrefixes — and Umbraco's own DeliveryApi:DisallowedContentTypeAliases.
  2. Not published, or no public URL for the culture → unknown (the tab stays).
  3. A template assigned → page.
  4. Evidence from installed packages (IFcPageEvidenceSource; Analytics answers "this URL has pageviews") → page.
  5. The front end itself: HEAD/GET on the public URL — 200 with HTML is a page, 404 is data. The public site is Flowcourier:Pages:SiteUrl, else a package's knowledge (IFcPublicSiteUrlSource; Analytics gives the tracked URL), else Umbraco's own URL — unless the Delivery API is enabled, in which case the front end is assumed to live elsewhere and nothing is probed.
  6. Otherwise unknown: surfaces stay visible with their own "no data" notices.

Verdicts are cached (24 h; 1 h for unknown) and shown under Settings → Flowcourier → Page detection. On a headless site, list the page types in IncludeDocumentTypes or set SiteUrl to the front end; both beat every heuristic.

{ "Flowcourier": { "Pages": { "ExcludeDocumentTypes": ["siteSettings", "dataFolder"], "SiteUrl": "https://www.example.com" } } }

Asset caching

Foundation serves every /App_Plugins/fc-* file with Cache-Control: no-cache: browsers keep the files but revalidate them on each load (a 304 for unchanged ones), so a package upgrade or a file edit shows up at once. Umbraco only cache-busts the files a manifest names, and the modules those import would otherwise stay stale under heuristic caching. Applies to all Flowcourier packages since they all reference Foundation.

Brand icons

<fc-brand-icon> shows the logo of a referrer, search engine, social network or AI assistant from a curated set vendored from the thesvg package (see VENDORED.md for the list and how to extend it).

import "/App_Plugins/fc-foundation/fc-brand-icon.element.js";
<fc-brand-icon name="Google"></fc-brand-icon>
<fc-brand-icon name="news.ycombinator.com" url="https://news.ycombinator.com/"></fc-brand-icon>
<fc-brand-icon name="GitHub" hover-scope="uui-table-row"></fc-brand-icon>
<fc-brand-icon name="SomeUnknownBot" fallback="icon-chip"></fc-brand-icon>

name is a brand name, slug or domain; url is tried first by host. Unknown brands render nothing, unless fallback names an Umbraco icon to stand in for them — which keeps a column of logos aligned where some rows have no brand (AEO marks unrecognised AI crawlers that way). At rest the icon is single-colour in the surrounding text colour; it becomes the coloured brand mark while hovered — the element itself, or the nearest ancestor matching hover-scope. color forces the coloured mark; --fc-brand-icon-size sets the size (16px). The FcBrandIcons export offers the same lookup (resolve, url) for code that needs an image URL, such as chart labels.

Date-range picker

A shared <fc-date-range> element for every date-ranged Flowcourier view: a trigger button showing the selected range, and a popover with presets on the left and two month calendars (From / To, with month and year selects) on the right, plus Clear / Cancel / Apply — Umbraco UI Library styling throughout.

import "/App_Plugins/fc-foundation/fc-date-range.element.js";
<fc-date-range
    .value=${{ from: "2026-08-20", to: "2026-09-18", preset: "last30" }}
    @change=${(e) => load(e.detail)}></fc-date-range>

change fires with { from, to, preset } — ISO dates (yyyy-MM-dd, local calendar days, no time zones) and the preset id or "custom". Clicking a preset applies immediately; picking days needs Apply. Options: .presets (your own [{ id, label, range() }]; the built-in list is exported as DEFAULT_PRESETS from fc-date-range.dates.js), min / max ISO bounds (max defaults to today), week-start (1 Monday default, 0 Sunday), clearable (Clear + Apply emits an empty range), compact, disabled. The date helpers (monthGrid, formatRange, presets…) are a separate pure module for reuse and testing.

Licensing (for paid Flowcourier features)

Foundation validates the license keys that unlock paid Flowcourier capabilities (e.g. Translation Pro) — fully offline, no license server, no phone-home. A key is a signed FC1.… token installed in configuration:

{ "Flowcourier": { "Licenses": { "TranslationPro": "FC1.eyJ..." } } }

(or the equivalent environment variable). Keys are bound to your domains (local development hosts always work) and to the Umbraco LTS cycle they were purchased for; trial keys simply carry an expiry. Whatever the license state, your site keeps working and your data stays readable and exportable — an invalid or missing key only means the paid features behave like the free package.

A package registers its product in its composer and gates its features through IFcLicenses:

builder.AddFlowcourierLicense("translation-pro", "Translation Pro");
// in a gated service:
if (!_licenses.Get("translation-pro").CheckFeature().IsLicensed) { /* degrade */ }

Dashboards surface the state with the shared banner element — it renders nothing while the product is licensed:

<fc-license-banner product-id="translation-pro"></fc-license-banner>

Requirements

Umbraco 17 (net10.0).

License

Free to use, including commercially — not open source. See the LICENSE.md packed with this package. Paid Pro features and consulting are available from Flowcourier ApS.

© 2026 Flowcourier ApS. All rights reserved.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on Flowcourier.Umbraco.Foundation:

Package Downloads
Flowcourier.Umbraco.AEO

Answer Engine Optimization for Umbraco. Exposes your published content to LLMs and AI answer engines by dynamically serving an /llms.txt index, an /llms-full.txt full-content dump, and a Markdown rendering of any page when the URL ends in ".md" — all generated from the live content cache, no editor work required. Also auto-injects schema.org JSON-LD (Organization, WebSite, Article, breadcrumbs, FAQ) into every page and ships an "AI Crawlers" backoffice dashboard that tracks visits from GPTBot, ClaudeBot, PerplexityBot and 150+ other AI crawlers (privacy-safe: analytics store no IPs by default — truncated/hashed storage is opt-in). Classifies crawler intent (training, AI search, user-triggered, scanner), records AI referrals — humans clicking through from claude.ai, chatgpt.com, perplexity.ai — and correlates them into "questions answered" conversions on an AI Referrals dashboard. Verifies claimed crawlers against vendor-published IP ranges and reverse DNS, blocks spoofers (vulnerability scanners wearing AI user agents) with an escalating in-memory-to-durable block list, and ships a Blocking dashboard to block or allow any crawler permanently. Zero-config install via an Umbraco pipeline filter; fully configurable through appsettings.

Flowcourier.Umbraco.AI.Agents.Translation

Translation Agent for the Umbraco AI Copilot: translates the open page into a target culture (creating the variant) and offers a send-for-approval flow. Ships the tool, its backoffice renderer, and seeds a named "Translation Agent" into the Copilot. Built on Flowcourier.Umbraco.AI.Core.

Flowcourier.Umbraco.AI.Agents.Seo

SEO Agent for the Umbraco AI Copilot: reads the open page, identifies its SEO/metadata fields and suggests optimised values, staged for review. Ships the tool, its backoffice renderer, and seeds a named "SEO Agent" into the Copilot. Built on Flowcourier.Umbraco.AI.Core.

Flowcourier.Umbraco.Analytics.Core

Shared base for Flowcourier's analytics packages: the "Analytics" section in the Umbraco backoffice (Realtime, Visitors, Behaviour, Acquisition, Technology dashboards), the "Analytics" tab on every document, and the optional Google PageSpeed Insights and Google Search Console reports. Traffic data comes from a provider package — install Flowcourier.Umbraco.Matomo for Matomo or Flowcourier.Umbraco.GoogleAnalytics for Google Analytics 4. Configured entirely from appsettings; the only database table is the index-status cache, created just for sites that turn Search Console index tracking on.

Flowcourier.Umbraco.Analytics.AI

Makes the Flowcourier Analytics data answerable in the Umbraco AI Copilot: tools for a page's traffic, its flow, search queries, index status, AI crawler activity and findings, plus an "Explain and suggest" action that turns a page's findings into three prioritised edits. Every tool returns aggregates only — never individual visitors, visitor ids or IP addresses. Needs Flowcourier.Umbraco.Analytics.Core, a provider package and the Umbraco AI Copilot. Also AI Visibility (Analytics Pro): your prompt set sampled through your own Umbraco AI chat profiles with web search, and what the answers cite, beside AEO's real AI-crawler fetches.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
17.5.8 135 9/24/2026
17.5.6 309 8/30/2026
17.5.5 393 8/1/2026
17.5.4 178 7/18/2026
17.5.3 153 7/15/2026
17.5.2 127 7/14/2026
17.5.1 116 7/14/2026
17.5.0 194 7/10/2026
17.0.0-alpha.1 108 7/4/2026