AgriBackup 1.1.3

dotnet add package AgriBackup --version 1.1.3
                    
NuGet\Install-Package AgriBackup -Version 1.1.3
                    
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="AgriBackup" Version="1.1.3" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AgriBackup" Version="1.1.3" />
                    
Directory.Packages.props
<PackageReference Include="AgriBackup" />
                    
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 AgriBackup --version 1.1.3
                    
#r "nuget: AgriBackup, 1.1.3"
                    
#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 AgriBackup@1.1.3
                    
#: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=AgriBackup&version=1.1.3
                    
Install as a Cake Addin
#tool nuget:?package=AgriBackup&version=1.1.3
                    
Install as a Cake Tool

AgriBackup C# Library

NuGet

The AgriBackup C# library provides convenient access to the AgriBackup Enterprise API from applications written in C#. It handles cryptographic authentication, deterministic network routing, and executes the complete TRACES NT compliance lifecycle.

Documentation

See the AgriBackup Documentation for C#.

Installation

Install the package via NuGet:

dotnet add package AgriBackup

Usage

The package needs to be configured with your account's API key, which you can get at https://agribackup.com. The SDK automatically routes your requests to the correct environment (Sandbox or Production) based on your key's prefix.

With this decoupled architecture, the SDK cleanly separates the environment setup (Webhooks) from the actual physical compliance flow.

Phase 1: Pre-assessment (The Sandbox Check)

Action: client.RiskManagement.AssessCoordinateRiskAsync(...)

Purpose: A rapid, synchronous Boolean check to verify if a coordinate is in a deforested zone before you spend capital or compute on heavy satellite ingestion.

using System;
using System.Threading.Tasks;
using AgriBackup.Api;
using AgriBackup.Client;
using AgriBackup.Model;
using Newtonsoft.Json;

namespace AgriBackupExample
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var client = new AgriBackupClient("sk_test_YOUR_API_KEY");
            var request = new CoordinateRiskRequest(latitude: -1.246807, longitude: 36.743217);
            
            var riskCheck = await client.RiskManagement.AssessCoordinateRiskAsync(request);

            if (riskCheck.DeforestationDetected == true) {
                Console.WriteLine("Deforestation detected. Cannot proceed.");
            } else {
                Console.WriteLine($"Coordinate is safe. Risk level: {riskCheck.CountryRiskLevel}");
            }
            Console.WriteLine(JsonConvert.SerializeObject(riskCheck));
        }
    }
}

Phase 2: Event-Driven Infrastructure (One-Time Setup)

Action: client.Webhooks.RegisterWebhookAsync(...)

Purpose: Establishes the enterprise routing for asynchronous fulfillment. You register your ERP endpoint to listen for polygon.verified, batch.risk_assessed, shipment.linked, and dds.submitted.

using AgriBackup.Model;
using System.Collections.Generic;

// Register your webhook endpoint once during system startup
var request = new WebhookRegistrationRequest(
    targetUrl: "https://your-erp.internal.co/api/webhooks/agribackup",
    eventTypes: new List<string> { "batch.risk_assessed", "polygon.verified", "shipment.linked", "dds.generated", "dds.submitted" }
);

var response = await client.Webhooks.RegisterWebhookAsync(request);
Console.WriteLine($"Webhook Secret (Save securely!): {response.SigningSecret}");
Webhook Event Payloads

Every webhook shares a common envelope (eventType, eventId, timestamp, attempt, nextRetry, data). Below are the schemas for the inner data object for each event:

  • polygon.verified: { jobId, polygonsVerified, polygonsFailed, status, polygonIds }
  • batch.risk_assessed: { batchId, batchCode, workflowId, riskScore, classification, status }
  • shipment.linked: { batchId, shipmentReference, transactionHash }
  • dds.generated: { jobId, batchId, ddsReference, status, error }
  • dds.submitted: { batchId, ddsReference, status }
  • dds.validated: { batchId, ddsReference, validationTimestamp, status }
  • dds.rejected: { batchId, ddsReference, rejectionReason, status }
  • job.failed: { jobId, jobType, errorCode, errorMessage }
  • report.ready: { reportId, reportType, downloadUrl }

Verifying Incoming Webhooks Use your SigningSecret to cryptographically verify that incoming webhooks originated from AgriBackup:

using System.Security.Cryptography;
using System.Text;

public bool VerifyWebhook(string signatureHeader, string rawBodyString, string secret)
{
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(rawBodyString));
    var hashString = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(hashString), 
        Encoding.UTF8.GetBytes(signatureHeader)
    );
}

Alternative: Manual Polling If you prefer not to use webhooks, or need to manually verify a job's status, you can retrieve the job state at any time:

var jobResponse = await client.Jobs.GetJobStatusAsync("job-1234-uuid");
var phase = jobResponse.Phase;

if (phase == "COMPLETED") {
    Console.WriteLine($"Job completed! Compliant units: {jobResponse.CompliantUnits}");
} else if (phase == "FAILED") {
    Console.WriteLine($"Job failed. Errors: {string.Join(", ", jobResponse.Errors)}");
} else {
    Console.WriteLine($"Job is still processing. Current phase: {phase}");
}

Phase 3: The Complete EUDR Execution Lifecycle

This is the core operational loop where the decoupling shines.

  1. Ingest Polygons: Call client.Polygons.IngestPolygonsAsync(...). (Async: wait for polygon.verified webhook).
  2. Register Batch: Call client.Batches.RegisterBatchAsync(...) using the verified polygon IDs. (Sync: returns batchId instantly).
  3. Attach Documentation: Call client.Documents.UploadAsync(...) or equivalent to bind legal EUDR documents to the batchId.
  4. Assess Batch Risk: Call client.Batches.AssessRiskAsync(...). (Async: wait for batch.risk_assessed webhook).
  5. Link Logistics: Call client.Logistics.LinkBatchToShipmentAsync(...). (Async: wait for shipment.linked webhook).
  6. Generate Declaration: Call client.Declarations.GenerateDdsAsync(...). (Async: wait for dds.generated webhook).
  7. Submit Declaration: Call client.Declarations.SubmitDdsAsync(...). (Async: wait for TRACES NT dds.validated webhook).

This SDK structure gives you absolute deterministic control over the state machine of your agricultural supply chain.

Complete Lifecycle Implementation Example
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using AgriBackup.Api;
using AgriBackup.Client;
using AgriBackup.Model;
using Newtonsoft.Json;

[ApiController]
[Route("api")]
public class WebhookController : ControllerBase
{
    private readonly AgriBackupClient _client = new AgriBackupClient("sk_test_YOUR_API_KEY");

    // ==========================================
    // Webhook Listener (Handling Async Events)
    // ==========================================
    [HttpPost("webhooks/agribackup")]
    public IActionResult HandleWebhook([FromBody] dynamic payload)
    {
        string eventType = payload.eventType;
        var data = payload.data;

        // Process asynchronously
        Task.Run(() => ProcessEvent(eventType, data));

        // 1. Acknowledge receipt immediately
        return Ok("OK");
    }

    // ==========================================
    // The State Machine Workflow
    // ==========================================
    private async Task ProcessEvent(string eventType, dynamic data)
    {
        try
        {
            switch (eventType)
            {
                case "polygon.verified": await HandlePolygonVerified(data); break;
                case "batch.risk_assessed": await HandleBatchRiskAssessed(data); break;
                case "shipment.linked": await HandleShipmentLinked(data); break;
                case "dds.generated": await HandleDdsGenerated(data); break;
                case "dds.submitted": Console.WriteLine($"DDS Successfully Filed! Ref: {data.ddsReference}"); break;
            }
        }
        catch (Exception ex) { Console.WriteLine($"Error processing webhook: {ex.Message}"); }
    }

    // Step 1: Ingest Polygons (Triggered manually or via ERP)
    public async Task StartComplianceFlow()
    {
        var request = new PolygonIngestionRequest(
            features: new List<GeoJsonFeature> {
                new GeoJsonFeature(
                    type: "Feature",
                    geometry: new GeoJsonGeometry(type: GeoJsonGeometry.TypeEnum.Polygon, coordinates: new List<List<List<double>>> { /* ... */ }),
                    properties: new FeatureProperties(farmerName: "Global Coffee Farmer #1", farmerId: "TEST_100", plotName: "Nyeri Hill Farm Block B", area: 2.5m, commodity: "Coffee")
                )
            }
        );
        var response = await _client.Polygons.IngestPolygonsAsync(request);
        Console.WriteLine($"Polygon ingestion started. Job ID: {response.JobId}");
    }

    // Step 2 & 3: Register Batch & Attach Documents
    private async Task HandlePolygonVerified(dynamic data)
    {
        if (data.status != "COMPLETED") return;

        var batchRequest = new BatchRegistrationRequest(
            commodity: "Coffee", countryCode: "KEN", hsCode: "0901", quantityKg: 1500.0m,
            polygonIds: data.polygonIds.ToObject<List<string>>(), vendorIds: new List<string> { "ERP-VEND-991" }
        );
        var batch = await _client.Batches.RegisterBatchAsync(batchRequest);

        // Step 4: Assess Batch Risk
        await _client.Batches.AssessRiskAsync((string)batch.BatchId);
        Console.WriteLine($"Batch risk assessment started for {batch.BatchId}");
    }

    // Step 5: Link Logistics
    private async Task HandleBatchRiskAssessed(dynamic data)
    {
        if (data.status != "COMPLETED" || data.classification == "HIGH_RISK") return;

        var linkReq = new ShipmentLinkRequest(
            batchId: (string)data.batchId, shipmentId: "SHP-123", billOfLading: "BOL-99281744", vesselName: "Evergreen"
        );
        await _client.Logistics.LinkBatchToShipmentAsync(linkReq);
    }

    // Step 6: Generate Declaration
    private async Task HandleShipmentLinked(dynamic data)
    {
        // Note: shipment.linked webhook firing indicates success. No status check required.

        var ddsReq = new DdsGenerationRequest(
            batchId: (string)data.batchId, legalDocumentHashes: new List<string> { "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }
        );
        var dds = await _client.Declarations.GenerateDdsAsync(ddsReq);
        Console.WriteLine($"DDS generation started for batch {data.batchId}. Job ID: {dds.JobId}");
    }

    // Step 7: Submit Declaration
    private async Task HandleDdsGenerated(dynamic data)
    {
        if (data.status != "COMPLETED") return;

        string ddsRef = (string)data.ddsReference;
        await _client.Declarations.SubmitDdsAsync(ddsRef);
        Console.WriteLine($"DDS submitted to TRACES. Awaiting validation for {ddsRef}...");
    }
}

Cryptographic Evidence

AgriBackup anchors all critical compliance states to the Hedera Hashgraph DLT. You can retrieve immutable, cryptographically verifiable proofs of your compliance events at any time.

using Newtonsoft.Json;

var evidence = await client.Evidence.GetBatchLedgerEvidenceAsync("batch-123");
Console.WriteLine(JsonConvert.SerializeObject(evidence, Formatting.Indented));

Interpreting the Evidence

The returned evidence object contains a chronological history of the entity's lifecycle anchored on-chain. It includes a list of stateProofs, where each proof represents a distinct compliance event (like CREATED or RISK_ASSESSED).

Key fields inside each state proof include:

  • hederaTransactionId: The exact identifier on the Hedera Consensus Service. You can search this ID on any public Hedera explorer (e.g., Hashscan) to independently verify the transaction.
  • consensusTimestamp: The decentralized, network-agreed time the event was permanently recorded.
  • operationType: The specific state transition that occurred.
  • merkleProof: The cryptographic data required to perform offline verification, ensuring the state has not been tampered with since anchoring.

Archiving Reports

AgriBackup supports asynchronous generation of bulk compliance archives. This compiles all XML payloads, Hedera state proofs, and legal document hashes into a single cryptographic ZIP artifact.

  1. Request the Archive: Call client.Archival.TriggerArchiveReportAsync with a date range.
  2. Await the Webhook: Wait for the report.ready webhook.
  3. Download: Use the provided URL or SDK method to securely retrieve the artifact.
using System;

// Step 1: Request Archive
var req = new ArchiveReportRequest(
    startDate: DateTime.Parse("2026-01-01"),
    endDate: DateTime.Parse("2026-03-31")
);
var response = await client.Archival.TriggerArchiveReportAsync(req);
Console.WriteLine($"Report job started: {response.ReportId}");

// Step 2: Handle Webhook (fired by report.ready)
public void HandleReportReady(dynamic data)
{
    if (data.reportType != "COMPLIANCE_ARCHIVE") return;
    Console.WriteLine($"Report is ready to download at: {data.downloadUrl}");
    
    // Optionally fetch it directly using the SDK:
    // var zipStream = await client.Archival.DownloadArchiveReportAsync((string)data.reportId);
}

Advanced Enterprise Configuration

Overriding Network Routing & Proxies

The client can be initialized with several options to bypass default network behaviors. This is primarily used by enterprise architectures operating behind zero-trust firewalls or corporate VPC proxies.

var client = new AgriBackupClient(
    "sk_live_...",
    "https://custom-proxy.internal.co" // Overrides automated prefix routing
);

Manual Idempotency Control

AgriBackup strictly guarantees safety during distributed failures via Idempotency-Key tracking. The backend will automatically generate this key if absent, so standard integrations can safely ignore this parameter.

If your Tier-1 enterprise architecture strictly requires passing your own internal ERP database transaction IDs as idempotency keys, you can inject them securely using the client's default HTTP headers:

// Set the Idempotency-Key globally for the transaction
client.Configuration.DefaultHeaders.Add("Idempotency-Key", "erp-tx-10928-abc");

var polygonRequest = new PolygonIngestionRequest { Features = new List<GeoJsonFeature> { ... } };
await client.Polygons.IngestPolygonsAsync(polygonRequest);

Support

If you require integration support or dedicated VPC configurations, reach out to support@agribackup.com or visit https://docs.agribackup.com.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.3 120 7/12/2026
1.1.2 106 7/12/2026
1.1.1 107 7/12/2026

Minor update