scoutnotebook.com · C# SDK · install & feed configuration
VisionFI.Scout

Install the Scout C# SDK

VisionFI.Scout is a NuGet package that runs the Scout engine in your own process — create notebooks, ingest loan documents, run recipes, read the output back — with no server and no cloud round-trip. Install it by committing a nuget.config that points at the VisionFI feed, then adding the package.

Package: VisionFI.Scout Embedded & offline OCR bundled Stable + Insiders channels
Prerequisites

01Before you start

Three things, and only the third one needs a person.

Note

VisionFI.Scout is not published on nuget.org. It comes from the VisionFI feed below. If a restore says the package can’t be found, the feed isn’t configured — that is very nearly always the whole problem.


The install

02Commit a nuget.config

Put this file at your repository root, next to the .sln, and commit it. Every developer who clones the repo and every CI runner that builds it then resolves the package the same way, with no per-machine setup.

nuget.config repo root · next to the .sln · commit it
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="visionfi" value="https://downloads.scoutnotebook.com/dist/scout-main/nuget/index.json" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
    <packageSource key="visionfi">
      <package pattern="VisionFI.*" />
    </packageSource>
  </packageSourceMapping>
</configuration>

Then, from the project that needs it:

dotnet add package VisionFI.Scout # NuGet resolves the current release from the feed — no version to look up

Why <clear />

It discards whatever package sources that machine has inherited — a global NuGet.Config, a Visual Studio setting, a leftover from another project. Restore then depends on this committed file alone, so it is deterministic instead of a function of developer-local state.

Why packageSourceMapping

It binds VisionFI.* to our feed and nothing else. Without it, the package ID is resolvable from any configured source — so anyone who registers that ID on nuget.org could get their code into a partner’s build. This is the standard dependency-confusion mitigation, and it is the reason the mapping block is not optional.

Not this: dotnet nuget add source … writes to the developer’s machine-level config. It’s invisible to anyone reading the repo, it doesn’t reach CI, and it doesn’t reach the next teammate who clones. Fine for a throwaway experiment on your own box; never the way a project is set up.


Release channels

03Stable and Insiders

Two feeds, one package ID. Stable is what production builds against; Insiders is a nightly prerelease train for teams who want the engine as it lands.

ChannelCadenceFeedHow you get it
Stable Its own cadence /dist/scout-main/nuget/index.json dotnet add package VisionFI.Scout
Insiders Nightly, 09:00 UTC /dist/scout-insiders/nuget/index.json dotnet add package VisionFI.Scout --prerelease

To use Insiders, add the second source and give it the same VisionFI.* mapping — a pattern can map to more than one source, and NuGet will consider both:

nuget.config — both channels same file, two sources, two mappings
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="visionfi" value="https://downloads.scoutnotebook.com/dist/scout-main/nuget/index.json" />
    <add key="visionfi-insiders" value="https://downloads.scoutnotebook.com/dist/scout-insiders/nuget/index.json" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
    <packageSource key="visionfi">
      <package pattern="VisionFI.*" />
    </packageSource>
    <packageSource key="visionfi-insiders">
      <package pattern="VisionFI.*" />
    </packageSource>
  </packageSourceMapping>
dotnet add package VisionFI.Scout # resolves Stable — prerelease versions are ignored unless you ask for them dotnet add package VisionFI.Scout --prerelease # opts into the nightly Insiders train

With both sources configured, a plain dotnet add package still resolves Stable; --prerelease is the opt-in. Insiders builds carry a prerelease suffix, which is what keeps them out of the way of a normal restore.

Both feed URLs above are permanent. Partners move onto each new Stable release automatically as it publishes — nothing on their side changes, and there is no URL to update, ever.


Version discipline

04Pin an exact version in production

For anything you ship, pin. Don’t use a floating range.

The package version is the version of the native engine inside it — the two are stamped together and cannot drift apart. That’s a useful property, and it’s also the reason a float is dangerous here: a floating range silently swaps the engine underneath your application, not just the managed wrapper around it.

YourProject.csproj replace the placeholder with the version you qualified
<PackageReference Include="VisionFI.Scout" Version="x.y.z" />

Multi-project solutions

Manage the version in one place with central package management: set ManagePackageVersionsCentrally in a Directory.Packages.props at the repo root, declare the version there once, and let each project reference the package without a version attribute.

Directory.Packages.props repo root · one version for every project
<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>
  <ItemGroup>
    <PackageVersion Include="VisionFI.Scout" Version="x.y.z" />
  </ItemGroup>
</Project>

Upgrading is then a one-line change in one file, reviewed like any other change — which is the point.


What to expect

05The first restore is a big download

This package is large — well over a hundred megabytes per version. That surprises people on a first restore. Nothing is wrong.

Each version carries the native Scout engine built for linux-x64, osx-arm64 and win-x64, plus the OCR data, in the one package. That’s the trade: a single artifact that works on every platform your team and your CI runners use, with no per-OS install step and no OCR setup. Budget for it in CI cache sizing and on metered connections.


First run

06Confirm what you actually installed

One line, and it belongs in your startup logs permanently.

Console.WriteLine(VisionFI.Scout.BuildInfo.Describe());

It returns a single line in the shape VisionFI.Scout <version> (<channel>; scout-agent <tag> @ <sha>) — the package version, the channel it came from (stable or insiders), and the exact engine commit bundled inside it.

Why this exists

The managed assembly and the native engine it binds are built from two different repositories. We hit a real incident where the C# compiled and ran fine, no error anywhere — but the bundled engine was stale, and a whole feature silently did nothing. Nothing in the package said which engine was inside it. Describe() is how you find that in a minute instead of a day.

The end-to-end shape

Once the package restores, the whole flow is a handful of await lines. Arguments are plain C#; the SDK handles all marshalling into the native engine internally.

C# — one loan package, start to finish async / await
using VisionFI.Scout;

// Initialize once — point at where data lives on disk.
await using var scout = await ScoutKit.InitAsync(new ScoutKitConfig {
    ConfigDir    = "/data/scout/config",      // catalog + settings
    NotebooksDir = "/data/scout/notebooks",   // the .scoutnb files
});

// Provision from Scout HQ — one call, once per install (and to refresh).
// Pulls the recipe catalog and everything a run needs. Just the token in.
await scout.SyncAsync(hqToken);

// 1. A notebook is the unit of work: one loan file or package.
var meta     = await scout.Notebooks.CreateAsync("United LOC");
var notebook = scout.Notebook(meta.Id);

// 2. Ingest — stage every document of the package.
foreach (var path in Directory.GetFiles("/loans/united-loc", "*.pdf"))
    await notebook.StagedFiles.StageFileAsync(path);

// 3. Process — OCR + classify. This must finish before a recipe runs.
await notebook.ProcessAsync();

// 4. Run a recipe (a key from Catalog.ListRecipesAsync()).
var run = await notebook.Runs.RunRecipeAsync("loan-terms-summary");

// 5. Read the report back out.
var reports = await notebook.Artifacts.ListByTypeAsync("loan-terms-summary");
string? report = await notebook.Artifacts.Get(reports[^1].Id).ReadTextAsync();
Console.WriteLine(report);
01Create

Open a notebook for the loan file.

02Stage

Put each document in. StageFileAsync

03Process

OCR + classify. ProcessAsync

04Run

Execute the recipe. RunRecipeAsync

05Read

Pull the report. Artifacts

The one ordering rule

Ingest must finish before the recipe runs — a recipe reads the classified documents, which only exist after processing completes. The API makes this natural: you await each step.


Support

07If something is wrong

Send us the output of BuildInfo.Describe() with your first message. It is the single most useful line in any Scout SDK bug report, and it usually shortens the conversation by a day.