CSharpAmazonBusinessAPI 0.1.4

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

β˜•Amazon Business API C# πŸš€ .NET NuGet

.NET C# library for the Amazon Business API. Wraps all 9 REST APIs (Document, Cart, Ordering, Product Search, Reconciliation, Reporting v2025-06-09 + v2021-01-08, User Management, Package Tracking, Application Management) behind a single AmazonBusinessConnection. LWA token refresh, rate-limit retry, regional endpoint routing, sandbox toggle, and debug logging are handled for you.

The generated clients are produced by NSwag from Amazon's published OpenAPI specs (fetched verbatim from developer-docs.amazon.com); the wrapper layer hides the per-spec type duplication so callers see Country.US instead of NSwag-generated Region2/Region3/etc.

Contents β€” Quick start Β· Configuration Β· Service surface Β· Onboarding (OAuth consent) Β· Roles Β· Troubleshooting Β· Status


Installation NuGet NuGet Downloads

Latest version is published on nuget.org. Install with the .NET CLI:

dotnet add package CSharpAmazonBusinessAPI

…or via the legacy Package Manager Console:

Install-Package CSharpAmazonBusinessAPI

…or in your .csproj directly:

<PackageReference Include="CSharpAmazonBusinessAPI" Version="*" />

Targets netstandard2.0 β€” runs on .NET Framework 4.6.1+, .NET Core 2.0+, and every modern .NET (.NET 5/6/7/8/9/10).

Quick start

using CSharpAmazonBusinessAPI;
using CSharpAmazonBusinessAPI.Utils;

var connection = new AmazonBusinessConnection(new AmazonBusinessCredential
{
    ClientId     = "amzn1.application-oa2-client.XXXX",
    ClientSecret = "XXXX",
    RefreshToken = "Atzr|XXXX",
    MarketPlace  = MarketPlace.UnitedStates,
    // Environment = AmazonBusinessCredential.Environments.Sandbox,  // for testing
});

// First call β€” token refresh, regional host, auth header are all handled.
var reports = await connection.Documents.GetReportsAsync(
    createdSince: DateTime.UtcNow.AddDays(-7));

Console.WriteLine($"Got {reports.Reports?.Count ?? 0} report(s)");

See Configuration for proxy / sandbox / debug-logging options, Onboard a customer for the OAuth consent flow used during one-time customer onboarding, Troubleshooting when something doesn't behave as expected, and Source/CSharpAmazonBusinessAPI.SampleCode/ for a per-API sample for every wrapper.

Status & roadmap

The roadmap below covers the full Amazon Business developer surface (docs index) and mirrors the structure used in the sister Amazon-SP-API-CSharp project. Foundation work is required before the per-API service wrappers can be built.

Foundation / scaffolding
  • MarketPlace / Region / Country lookup tables β€” 11 supported markets (register-as-a-developer) across 3 regional hosts (ab-api-endpoints): na.business-api.amazon.com, eu.business-api.amazon.com, jp.business-api.amazon.com.
  • AmazonBusinessCredential β€” LWA-only (ClientId, ClientSecret, RefreshToken, MarketPlace / MarketPlaceID, optional Proxy, IsDebugMode, Environment). No AWS keys.
  • LWA token pipeline β€” LwaClient POSTs to https://api.amazon.com/auth/o2/token; per-credential AccessTokenCache (thread-safe via SemaphoreSlim, refreshes 60s before expiry); LwaAuthHandler DelegatingHandler injects x-amz-access-token and retries once on 401.
  • LWA client-secret rotation β€” AmazonBusinessCredential.RotateClientSecret(newSecret) swaps the secret in place and invalidates the cache so the next call re-exchanges.
  • AmazonBusinessConnection facade β€” validates credentials, resolves MarketPlaceID β†’ MarketPlace, builds a configured HttpClient, exposes one property per API surface (Documents, Cart, Ordering, ProductSearch, Reconciliation, Reporting, ReportingLegacy, PackageTracking, Users, Applications).
  • HTTP layer for NSwag-generated clients β€” chained handlers RateLimit β†’ Auth β†’ Debug β†’ HttpClientHandler wrap a shared HttpClient whose BaseAddress comes from MarketPlace.Region. Generated clients consume the HttpClient in their constructor.
  • Exception types β€” AmazonBusinessException base + Unauthorized, InvalidInput, NotFound, QuotaExceeded, InternalError subclasses. Each carries StatusCode + ResponseBody.
  • Rate-limit handling β€” RateLimitHandler DelegatingHandler honors Retry-After (delta or HTTP-date), exponential-backoff fallback, capped by MaxThrottledRetryCount; throws AmazonBusinessQuotaExceededException if exhausted.
  • Sandbox toggle β€” AmazonBusinessCredential.Environment (Sandbox/Production) switches BaseAddress between Region.HostUrl and Region.SandboxHostUrl.
  • Debug logging β€” DebugLogHandler pretty-prints request/response (with masked sensitive headers) when IsDebugMode == true. Routes through ILogger if AmazonBusinessConnection was constructed with an ILoggerFactory, else falls back to Console.
API surfaces

Each API has: (1) OpenAPI spec under Source/CSharpAmazonBusinessAPI/OpenAPIs/ (fetched by scripts/fetch_spec.py), (2) an <OpenApiReference> entry in the csproj, (3) a hand-written *Service wrapper exposed off AmazonBusinessConnection, (4) a sample under Source/CSharpAmazonBusinessAPI.SampleCode/, (5) sandbox-mode tests under Source/Tests/.

Workflows & integrations
  • REST cross-API sample β€” see CartToOrderSample.cs for Product Search β†’ Cart β†’ Ordering. Note: Amazon's Integrated Quoting workflow is a separate cXML/cert-auth integration, not a REST workflow β€” it's out of scope for this SDK.
  • Third-party website authorization β€” LwaConsentHelper.BuildAuthorizationUrl() + ExchangeCodeForTokensAsync(). Use during one-time onboarding to collect a customer's refresh token, then persist it for AmazonBusinessCredential. Runnable demo: see Source/CSharpAmazonBusinessAPI.WebAuthSample β€” ASP.NET Core minimal-API project with the full consent flow.
  • App Center authorization workflow β€” same LwaConsentHelper.ExchangeCodeForTokensAsync() covers the LWA token-exchange step. The Amazon-initiated callback dance (amazon_callback_uri, amazon_state echoing, step-3 ack POST, step-6 return-to-App-Center redirect) is wired in the demo at /appcenter/login-uri and /appcenter/oauth/callback β€” see Source/CSharpAmazonBusinessAPI.WebAuthSample for the working reference.
  • Punch-in β€” out of scope (server-side cXML/SOAP-style endpoint your e-procurement system hosts, with TLS certs / shared-secret auth β€” same situation as Integrated Quoting). See Punch-in integration guide if you need to integrate the e-procurement side.
  • Amazon Business Integrations MCP Server β€” Amazon-hosted MCP server for AI assistants. Out of scope for the SDK; mentioned here for discoverability.
Sample app & tests
  • SampleCode/Program.cs loads credentials from appsettings.json + User Secrets and prints the resolved region/host/marketplace. Live calls per API are pre-wired and commented β€” uncomment after dropping in real credentials.
  • Tests/CSharpAmazonBusinessAPI.Tests β€” xUnit, 41 unit tests + 8 sandbox integration tests (read-only).
    • Unit: MarketPlace lookup + regional routing, AmazonBusinessConnection validation/marketplace resolution, AccessTokenCache (caching, invalidation, concurrent refresh, LWA failure), LwaAuthHandler (header injection, cache reuse, 401 retry-once with fresh token), RateLimitHandler (429 retry, Retry-After delta + HTTP-date, exhaustion β†’ AmazonBusinessQuotaExceededException), RotateClientSecret flow, ApiException shape, LwaConsentHelper URL-builder + arg validation.
    • Integration: SandboxIntegrationTests.cs smoke-tests Documents, Reconciliation, ReportingLegacy, Reporting (GetOrderReports / GetShipmentReports), ProductSearch, Cart, PackageTracking against the real sandbox. Skipped automatically when AB_INTEGRATION_* env vars aren't set. Destructive ops (Ordering.PlaceOrder, Users.CreateBusinessUserAccount, Applications.RotateApplicationClientSecret) are intentionally not tested here β€” add a separate suite when you opt in.
    • Run with dotnet test (or dotnet test --filter Category=Integration for just the sandbox tests).
  • Amazon Business roles reference β€” see the Roles required per API table in the Usage section. Roles are requested via the Developer Registration Access Form (DRAF) at app creation; the API surface won't authorize without the matching role.

Keys

Amazon Business uses Login with Amazon (LWA) only β€” no AWS IAM, no STS, no role ARN. Onboarding flow:

  1. Register as a developer and request the roles your app needs (Developer Registration Access Form).
  2. Create an app client in the Solution Provider Portal β€” you'll receive a ClientId + ClientSecret.
  3. Authorize your app (or use the website-authorization workflow) to obtain a long-lived RefreshToken per Amazon Business customer.
Field Description
MarketPlace / MarketPlaceID Target marketplace (list). Determines regional endpoint (NA/EU/FE).
ClientId Your app's amzn1.application-oa2-client.… ID.
ClientSecret Your app's secret (rotatable β€” see LWA client-secret rotation).
RefreshToken Long-lived token issued per customer after consent. Exchanged for a 1-hour access token automatically by the SDK.

Usage

Configuration

See Program.cs for a runnable example that loads credentials from appsettings.json and User Secrets.

var connection = new AmazonBusinessConnection(new AmazonBusinessCredential
{
    ClientId     = "amzn1.application-oa2-client.XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    ClientSecret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    RefreshToken = "Atzr|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    MarketPlace  = MarketPlace.UnitedStates,   // or: MarketPlaceID = "ATVPDKIKX0DER"
    Environment  = AmazonBusinessCredential.Environments.Sandbox,
    IsDebugMode  = true,
});

AmazonBusinessConnection exposes one property per API surface:

Property API
Documents Document API v1 (invoice reports)
Cart Cart API v1
Applications Application Management API v1
Ordering Ordering API v1
PackageTracking Package Tracking API v1
ProductSearch Product Search API v1
Reconciliation Reconciliation API v1
Reporting Reporting API v2025-06-09 (current)
ReportingLegacy Reporting API v2021-01-08 (legacy)
Users User Management API v1

Each service exposes typed methods plus a .Client property for direct access to the NSwag-generated client when you need parameters not on the wrapper.

Configuration with a proxy

var connection = new AmazonBusinessConnection(new AmazonBusinessCredential
{
    ClientId = "...", ClientSecret = "...", RefreshToken = "...",
    MarketPlaceID = "ATVPDKIKX0DER",
    Proxy = new System.Net.WebProxy("http://xxx.xxx.xxx.xxx:xxxx")
    {
        Credentials = new System.Net.NetworkCredential("username", "password"),
    },
});

For SaaS apps and App Center listings: send the customer to Amazon's consent page, then exchange the returned code for a long-lived refresh_token and persist it. A runnable end-to-end ASP.NET Core demo lives in Source/CSharpAmazonBusinessAPI.WebAuthSample β€” dotnet run from that folder, browse to https://localhost:7271, click Connect.

// 1. Generate a CSRF token, then redirect the customer's browser here:
var url = LwaConsentHelper.BuildAuthorizationUrl(
    clientId:    "amzn1.application-oa2-client.XXXX",
    redirectUri: "https://my.app/oauth/callback",
    state:       Guid.NewGuid().ToString());

// 2. On callback, verify state matches, then exchange the code:
var tokens = await LwaConsentHelper.ExchangeCodeForTokensAsync(
    code:         queryParams["code"],
    clientId:     "amzn1.application-oa2-client.XXXX",
    clientSecret: "...",
    redirectUri:  "https://my.app/oauth/callback");

// 3. Persist tokens.RefreshToken for this customer; pass it to AmazonBusinessCredential
//    going forward. (The access_token expires in 1h β€” the SDK handles renewal automatically.)

LWA client-secret rotation

// 1. Trigger Amazon to send a new secret to your registered SQS queue.
await connection.Applications.RotateApplicationClientSecretAsync();

// 2. After picking up the new secret, swap it in place β€” the cached access
//    token is invalidated automatically, so the next API call re-exchanges.
connection.Credentials.RotateClientSecret(newSecret);

Document API β€” list and download invoice reports

For more, see DocumentSample.cs.

// List recent invoice reports (marketplaceIds defaults to credential).
var reports = await connection.Documents.GetReportsAsync(
    createdSince: DateTime.UtcNow.AddDays(-30));

// Create an invoice report, then poll for completion + download.
var reportId = (await connection.Documents.CreateReportAsync(new CreateReportSpecification
{
    ReportType    = "GET_FLAT_FILE_VAT_INVOICE_DATA_REPORT",
    DataStartTime = DateTime.UtcNow.AddDays(-30),
    DataEndTime   = DateTime.UtcNow,
})).ReportId;

var report = await connection.Documents.GetReportAsync(reportId);
if (report.ProcessingStatus == ProcessingStatus.DONE)
{
    var doc = await connection.Documents.GetReportDocumentAsync(report.ReportDocumentId);
    // doc.Url is a 5-minute presigned URL; doc.CompressionAlgorithm == GZIP if compressed.
}

Cart API β€” list, get, add items

For more, see CartSample.cs. The country parameter defaults to the connection's marketplace β€” pass Country.X explicitly only to override per call.

// Country defaults from connection.Credentials.MarketPlace.Country.
var carts = await connection.Cart.ListCartsAsync(
    customerEmail: "buyer@example.com",
    pageSize: 25);

await connection.Cart.AddItemsAsync("cart-123", new AddItemsRequest
{
    Items = new List<AddItemRequest>
    {
        new AddItemRequest { ProductIdentifier = "B07HMBFZCZ", Quantity = 2 },
    },
});

Reporting API v2025-06-09

For more, see ReportingSample.cs. The 5 ops have wrapper methods (country defaults from connection); .Client is also there for the raw generated surface.

var orderReports = await connection.Reporting.GetOrderReportsAsync(
    orderStartDate: DateTimeOffset.UtcNow.AddDays(-7),
    orderEndDate:   DateTimeOffset.UtcNow);

Reconciliation API β€” pull transactions

For more, see ReconciliationSample.cs.

var transactions = await connection.Reconciliation.GetTransactionsAsync(
    feedStartDate: DateTimeOffset.UtcNow.AddDays(-30),
    feedEndDate:   DateTimeOffset.UtcNow);

Package Tracking

For more, see PackageTrackingSample.cs.

// Country defaults from the connection's marketplace.
var details = await connection.PackageTracking.GetPackageTrackingDetailsAsync(
    orderId:    "114-2589187-9801025",
    shipmentId: "401971789238301",
    packageId:  "1");

For more, see ProductSearchSample.cs. The two most common ops have wrappers; the other three (SearchOffersRequest, GetProductsByAsins, GetOffersByOfferIds) are reachable via .Client.

var results = await connection.ProductSearch.SearchProductsAsync(
    keywords:      "office chair",
    customerEmail: "buyer@example.com",
    pageSize:      24,
    sortKey:       SortKey.RELEVANCE);

var product = await connection.ProductSearch.GetProductByAsinAsync(
    asin:          "B07HMBFZCZ",
    customerEmail: "buyer@example.com");

Other surfaces

Enable debug mode

Set IsDebugMode = true on the credential. Pretty-prints request and response (with sensitive headers masked) for every outbound call. Pass an ILoggerFactory to AmazonBusinessConnection to route through ILogger instead of Console.

Roles required per API

Each API call needs a specific Amazon Business role granted at app-creation time via the Developer Registration Access Form. Calling without the matching role returns 403. (Source.)

Role API surfaces (this SDK)
Business Product Catalog connection.ProductSearch
Amazon Business Analytics connection.Reporting, connection.ReportingLegacy
Business Purchase Reconciliation connection.Reconciliation, connection.Documents
Amazon Business Order Placement connection.Ordering, connection.Cart, connection.PackageTracking
User Management (offline approval required) connection.Users
(no role required) connection.Applications β€” uses standard LWA scope

All roles are available across NA, EU, and FE marketplaces.

Troubleshooting

400 InvalidInput "Could not match input arguments" in sandbox

Amazon Business's static sandbox does exact parameter matching, not subset matching. Sending any extra query parameter β€” even a sensible-looking one like marketplaceIds β€” breaks the match. Each operation has a documented sandbox match block in its OpenAPI spec under responses.200.x-amzn-api-sandbox.static[].request.parameters; send only those parameters with their literal values to get a 200.

Common offenders this SDK fixes automatically:

  • Repeated query keys (?foo=a&foo=b) β€” NSwag's default; sandbox needs csv (?foo=a,b). Handled by CsvArrayRewriteHandler.
  • Bare ISO timestamps (2020-07-09T00:00:00) β€” NSwag's "s" format omits the timezone designator; sandbox needs the trailing Z. Handled by IsoDateTimeRewriteHandler.
  • Auto-defaulted marketplaceIds in Documents.GetReports β€” defaults from your credential's marketplace, but the sandbox doesn't expect it. Pass marketplaceIds: Array.Empty<string>() explicitly to skip the default for sandbox testing.
500 InternalFailure with empty details

Amazon's sandbox returns generic 500s for several scenarios where you'd hope for a clearer error. Check, in order:

  1. Role not granted on your sandbox app. Each API requires a specific role (see Roles required per API). Confirm in Solution Provider Portal β†’ your app β†’ Roles. The fastest signal: if some APIs work and others 500, it's role-shaped β€” APIs sharing a role will fail together.
  2. Cart in particular is broken in sandbox. Even with the documented ?region=US and cart-123 values, all Cart ops return 500 InternalFailure. This is an Amazon-side issue (not the SDK); production calls work fine. If you need this fixed, open a ticket via SPP support.
  3. Sandbox app not fully provisioned. If every API 500s, the sandbox refresh token may not be properly bound. Email Amazon Business Support with your applicationId and a failed x-amzn-RequestId.
Operations that don't actually work in sandbox (despite Amazon's docs claiming so)

These have no x-amzn-api-sandbox block in their OpenAPI spec β€” the sandbox doesn't mock them and will always 400:

  • ProductSearch.SearchProducts and the rest of Product Search
  • ReportingLegacy (Reporting v2021-01-08) β€” both operations

They work fine against production endpoints. For local validation, hit production with Environment.Production once you have approved production credentials.

Operations not available in sandbox at all

Per Amazon's sandbox docs, three APIs are intentionally not in sandbox because they're destructive:

  • connection.Ordering β€” would place real orders
  • connection.Applications.RotateApplicationClientSecretAsync β€” rotates your real production secret even from a sandbox-context call
  • connection.Users.CreateBusinessUserAccountAsync β€” creates real Amazon Business users

The SDK exposes them; you just need to opt into testing them deliberately against production.

You're sending the customer to https://www.amazon.com/ap/oa (the standard LWA endpoint) instead of https://www.amazon.<tld>/b2b/abws/oauth (the Amazon Business endpoint). Use LwaConsentHelper.BuildBusinessAuthorizationUrl(applicationId, redirectUri, state, country) β€” not BuildAuthorizationUrl. The Business endpoint takes the SPP applicationId (amzn1.sp.solution.*), not the LWA client_id, and accepts no scope parameter.

Your sandbox app doesn't go through the consent flow β€” the sandbox refresh token is generated directly in SPP (Action β†’ Create token), not via OAuth. The website-authorization workflow is for production customer onboarding, where real Amazon Business admins authorize your app for their org. Sandbox apps lack the registration metadata to consent against real accounts.

NSwag-generated ApiException<ErrorList> leaks into application code

If you see this, you're on a build before the ErrorTranslationHandler was wired in (pre-0.1.1). Upgrade to the current package β€” non-2xx responses now throw AmazonBusinessException subclasses (InvalidInput / Unauthorized / NotFound / QuotaExceeded / InternalError) carrying StatusCode + ResponseBody + a parsed Amazon-error message.

Push protection blocks git push with "Repository contains secrets"

You committed real LWA credentials. Rotate them in SPP first (they're already exposed). Then either re-create the bad commit without the secret files (git rm --cached <file>, amend or rebase) or use git filter-repo to scrub the file from the entire history. Don't take the "allow this secret" escape hatch β€” secrets in git history are permanent.

Out of scope: Integrated Quoting

Amazon's Integrated Quoting workflow is a separate cXML over HTTPS integration with digital-certificate auth, used by enterprise eProcurement sourcing modules β€” it does not use the REST APIs this SDK wraps. If you need that flow, integrate it via your cXML stack alongside this library.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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
0.1.4 100 4/26/2026
0.1.2 94 4/26/2026
0.1.0 90 4/26/2026