π .NET SDK β Quick Start¶
Get your first end-to-end flow running in minutes:
- Authenticate with API key
- Discover a Project and Service (or use environment variables)
- Upload a document
- Trigger processing
- Poll until done
- Extract and print results
NuGet: AIForged.SDK
Prerequisites¶
- .NET 6+ SDK
- An AIForged account with access to at least one Project and Service
- An API key
Unified access
No cloud-provider keys or regional setup required. AIForged is a unified access layerβyour AIForged account + API key is all you need.
Set environment variables (recommended)¶
Use environment variables to keep secrets out of source control and switch environments easily.
- AIFORGED_BASE_URL β Production API base URL: https://portal.aiforged.com
- AIFORGED_API_KEY β your API key
- AIFORGED_PROJECT_ID β optional; default project id for samples
- AIFORGED_SERVICE_ID β optional; default service id for samples
- AIFORGED_GROUP_ID β optional; default group for HITL (work items)
PowerShell:
$env:AIFORGED_BASE_URL = "https://portal.aiforged.com"
$env:AIFORGED_API_KEY = "β’β’β’"
# Optional shortcuts for this quick start
$env:AIFORGED_PROJECT_ID = "123"
$env:AIFORGED_SERVICE_ID = "456"
$env:AIFORGED_GROUP_ID = "789"
Bash/zsh:
export AIFORGED_BASE_URL="https://portal.aiforged.com"
export AIFORGED_API_KEY="β’β’β’"
export AIFORGED_PROJECT_ID="123"
export AIFORGED_SERVICE_ID="456"
export AIFORGED_GROUP_ID="789"
Optional (local dev) β .NET user secrets:
dotnet user-secrets set "AIFORGED_BASE_URL" "https://portal.aiforged.com"
dotnet user-secrets set "AIFORGED_API_KEY" "β’β’β’"
Security
- Treat your API key like a password. Donβt commit it to source control.
- Prefer environment variables or a secrets manager (Key Vault, AWS Secrets Manager).
- Never log the API key; redact secrets in telemetry.
Create a console app¶
mkdir aiforged-quickstart
cd aiforged-quickstart
dotnet new console
dotnet add package AIForged.SDK
Replace Program.cs with the code below, then run:
dotnet run
Program.cs (copy/paste)¶
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using AIForged.API;
class Program
{
static async Task<int> Main()
{
// 1) Read environment variables
var baseUrl = Env("AIFORGED_BASE_URL") ?? "https://portal.aiforged.com"; // production
var apiKey = Env("AIFORGED_API_KEY") ?? throw new Exception("AIFORGED_API_KEY not set.");
int? projectId = TryInt("AIFORGED_PROJECT_ID");
int? serviceId = TryInt("AIFORGED_SERVICE_ID");
// Optional: change file to your local test document
var filePath = "invoice.pdf";
// 2) Configure the SDK
var cfg = new Config
{
BaseUrl = baseUrl,
Timeout = TimeSpan.FromMinutes(5)
};
await cfg.Init(); // wires HttpClient/handler
cfg.HttpClient.DefaultRequestHeaders.Add("X-Api-Key", apiKey);
var ctx = new Context(cfg);
try
{
// 3) Verify we can call the API
var me = await ctx.GetCurrentUserAsync();
Console.WriteLine($"Connected as: {me?.Email ?? me?.UserName} ({me?.Id})");
// 4) Pick a Project and Service (env vars or auto-discover first available)
if (projectId == null || serviceId == null)
{
(projectId, serviceId) = await AutoDiscoverProjectAndServiceAsync(ctx);
if (projectId == null || serviceId == null)
throw new Exception("No accessible Project/Service found. Please set AIFORGED_PROJECT_ID and AIFORGED_SERVICE_ID.");
}
Console.WriteLine($"Using ProjectId={projectId}, ServiceId={serviceId}");
// 5) Upload a document
if (!File.Exists(filePath))
{
Console.WriteLine($"Sample file '{filePath}' not found. Create a PDF with this name or update filePath.");
return 0;
}
byte[] bytes = await File.ReadAllBytesAsync(filePath);
using var ms = new MemoryStream(bytes);
var fp = new FileParameter(ms, Path.GetFileName(filePath), "application/pdf");
var upload = await ctx.DocumentClient.UploadFileAsync(
stpdId: serviceId,
userId: ctx.CurrentUser.Id,
projectId: projectId,
classId: null,
status: DocumentStatus.Received,
usage: UsageType.Inbox,
masterid: null,
comment: "Quick Start upload",
externalId: $"QS-{Guid.NewGuid():N}",
result: null, resultId: null, resultIndex: null,
guid: Guid.NewGuid(),
data: fp
);
var doc = upload?.Result?.FirstOrDefault();
if (doc?.Id == null) throw new Exception("Upload failed: no document returned.");
Console.WriteLine($"Uploaded doc id: {doc.Id}");
// 6) Trigger processing
await ctx.ServicesClient.ProcessAsync(
userId: ctx.CurrentUser.Id,
projectId: projectId,
stpdId: serviceId,
docIds: new() { doc.Id }
);
Console.WriteLine("Processing started...");
// 7) Poll until done (simple exponential backoff)
var processed = await WaitUntilProcessedAsync(ctx, doc.Id, TimeSpan.FromMinutes(3));
if (processed == null)
{
Console.WriteLine("Timed out waiting for processing. Check status in Studio.");
return 0;
}
Console.WriteLine($"Final status: {processed.Status}");
if (processed.Status == DocumentStatus.Error)
{
Console.WriteLine("Document ended in Error. Review logs in Studio or via System/Log clients.");
return 0;
}
// 8) Extract and print results
var extraction = await ctx.ParametersClient.ExtractAsync(processed.Id);
Console.WriteLine("Results:");
foreach (var row in extraction?.Result ?? Enumerable.Empty<DocumentExtraction>())
{
Console.WriteLine($"- {row.Name}: {row.Value} (conf {row.Confidence})");
}
Console.WriteLine("Quick Start completed successfully!");
return 0;
}
catch (SwaggerException ex)
{
Console.Error.WriteLine($"API error {(int)ex.StatusCode} {ex.StatusCode}: {ex.Message}");
Console.Error.WriteLine($"Response: {ex.Response}");
if (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
Console.Error.WriteLine("Check X-Api-Key and permissions for the target project/service.");
return 1;
}
catch (Exception ex)
{
Console.Error.WriteLine($"Unexpected error: {ex}");
return 1;
}
}
static async Task<(int? projectId, int? serviceId)> AutoDiscoverProjectAndServiceAsync(Context ctx)
{
// Get all projects for the current user
var projects = await ctx.ProjectClient.GetByUserAsync(ctx.CurrentUser.Id, includeBalance: true);
var project = projects?.Result?.FirstOrDefault();
if (project == null) return (null, null);
// Get services in the selected project
var services = await ctx.ProjectClient.GetServicesAsync(ctx.CurrentUser.Id, project.Id, null, null);
var service = services?.Result?.FirstOrDefault();
if (service == null) return (project.Id, null);
return (project.Id, service.Id);
}
static async Task<DocumentViewModel?> WaitUntilProcessedAsync(Context ctx, int docId, TimeSpan timeout)
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var delay = TimeSpan.FromSeconds(2);
while (sw.Elapsed < timeout)
{
var resp = await ctx.DocumentClient.GetDocumentAsync(docId);
var d = resp?.Result;
if (d == null) return null;
if (d.Status is DocumentStatus.Processed
or DocumentStatus.InterimProcessed
or DocumentStatus.Verification
or DocumentStatus.Error)
return d;
await Task.Delay(delay);
var next = Math.Min(delay.TotalSeconds * 1.5, 30); // cap at ~30s
delay = TimeSpan.FromSeconds(next);
}
return null;
}
static string? Env(string name) => Environment.GetEnvironmentVariable(name);
static int? TryInt(string name) => int.TryParse(Env(name), out var v) ? v : null;
}
Optional
If your project uses implicit usings or a different SDK style, no changes are needed. The code above targets .NET SDK defaults.
What just happened?¶
- Initialized the SDK with BaseUrl https://portal.aiforged.com and your API key (X-Api-Key).
- Confirmed connectivity by fetching the current user.
- Selected a Project and Service (from env vars, or auto-discovered the first available).
- Uploaded a PDF into Inbox with status Received.
- Triggered processing for that document.
- Polled until terminal status (Processed, InterimProcessed, Verification, or Error).
- Extracted results and printed them.
Roles required
To create/configure services, you need the Owner, Administrator, or Developer role in the target tenant. Viewing/processing may vary by your organizationβs RBAC policies.
Troubleshooting¶
-
401/403 Unauthorized/Forbidden
- Verify AIFORGED_BASE_URL and that the API key is valid and active.
- Ensure the key has access to the Project and Service.
-
404 Not Found
- Double-check ProjectId/ServiceId; avoid mixing environments (e.g., Studio vs API base).
-
Timeouts or large files
- Increase
Config.Timeout
(e.g., 5β10 minutes for large PDFs). - Use retries with backoff for 429/5xx.
- Increase
-
Status remains Received
- Confirm Auto Execution isnβt disabled.
- Ensure the Service supports processing path for your document type.
Next steps
- Replace filePath with your own sample documents.
- Add minimal logging (ILogger) and trace IDs to correlate across services.
- Switch from polling to webhooks for production-scale flows.