Skip to content

qubitonhq/qubiton-dotnet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

QubitOn API — .NET SDK

.NET client for the QubitOn API. Validate addresses, tax IDs, bank accounts, phones, and emails; look up business registrations and corporate hierarchies; screen for sanctions, PEPs, disqualified directors, EPA prosecutions, and healthcare exclusions; assess credit, ESG, and entity risk.

Targets: netstandard2.0, net6.0, net8.0, net10.0.

Installation

dotnet add package Qubiton.Sdk

Or via PackageReference:

<PackageReference Include="Qubiton.Sdk" Version="0.2.0" />

Quick Start

The SDK is designed to be long-lived — allocate one QubitOnClient per process and reuse it (it owns connection pools and an OAuth token cache). The throwaway using shown here is fine for a one-shot program (CLI, script, console demo); for any service-like workload prefer the Dependency Injection section below or the Bring Your Own HttpClient pattern.

using Qubiton.Sdk;
using Qubiton.Sdk.Models;

// One-shot script — `using` is fine because the process exits right after.
using var client = new QubitOnClient(new QubitOnClientOptions { ApiKey = "svm..." });

var resp = await client.ValidateAddressAsync(new AddressRequest
{
    AddressLine1 = "123 Main St",
    City = "New York",
    State = "NY",
    PostalCode = "10001",
    Country = "US",
});

Console.WriteLine($"Standardized: {resp.Address1}, {resp.City} {resp.State} {resp.PostalCode}");

Authentication

The SDK supports two authentication modes — configure exactly one.

API Key (recommended)

The key is sent as the lowercase apikey header (the .NET API server expects this name; X-Api-Key is ignored).

var client = new QubitOnClient(new QubitOnClientOptions { ApiKey = "svm..." });

API Token Exchange (key + secret)

The SDK exchanges your {ClientId, ClientSecret} pair against POST /api/oauth/token for a short-lived bearer token, caches it, and refreshes automatically. On a 401 response the cached token is invalidated and the request is retried once with a fresh token.

var client = new QubitOnClient(new QubitOnClientOptions
{
    ClientId = "your-key",
    ClientSecret = "your-secret",
    // Optional: TokenUrl = "https://api.qubiton.com/api/oauth/token",
});

Dependency Injection

using Qubiton.Sdk;

services.AddQubiton(o =>
{
    o.ApiKey = builder.Configuration["Qubiton:ApiKey"];
    o.Timeout = TimeSpan.FromSeconds(30);
});

// Resolve in your code:
public class MyService(IQubitOnClient qubiton) { ... }

AddQubiton registers the client as a singleton backed by IHttpClientFactory, so the underlying socket pool is owned by the host. The SDK does not mutate the factory-managed HttpClient's Timeout — per-request timeouts are enforced internally via a linked CancellationTokenSource.

Bring Your Own HttpClient

Reuse a long-lived HttpClient (or one provided by IHttpClientFactory). Do not allocate a new HttpClient per call — that pattern leaks sockets and is the most common source of SocketException in production.

// Recommended: get an HttpClient from IHttpClientFactory.
var http = httpClientFactory.CreateClient("qubiton");
using var client = new QubitOnClient(new QubitOnClientOptions { ApiKey = "svm..." }, http);

// Acceptable: long-lived static HttpClient (one per process).
private static readonly HttpClient SharedHttp = new HttpClient();
using var client = new QubitOnClient(new QubitOnClientOptions { ApiKey = "svm..." }, SharedHttp);

When you supply an HttpClient the SDK uses it as-is and does not mutate its BaseAddress, DefaultRequestHeaders, or Timeout. The per-request timeout is enforced inside the SDK using a linked CancellationTokenSource so caller-supplied clients are never reconfigured.

Method Surface (47 methods)

Category Method Endpoint
Address ValidateAddressAsync POST /api/address/validate
Tax ValidateTaxAsync POST /api/tax/validate
Tax ValidateTaxFormatAsync POST /api/tax/format-validate
Tax GetSupportedTaxFormatsAsync GET /api/tax/format-validate/countries
Bank ValidateBankAccountAsync POST /api/bank/validate
Bank ValidateBankProAsync POST /api/bankaccount/pro/validate
Email ValidateEmailAsync POST /api/email/validate
Phone ValidatePhoneAsync POST /api/phone/validate
Business Registration LookupBusinessRegistrationAsync POST /api/businessregistration/lookup
Peppol ValidatePeppolAsync POST /api/peppol/validate
Peppol GetPeppolSchemesAsync GET /api/peppol/schemes
Sanctions CheckSanctionsAsync POST /api/prohibited/lookup
PEP ScreenPepAsync POST /api/pep/lookup
Directors CheckDirectorsAsync POST /api/disqualifieddirectors/validate
EPA CheckEpaProsecutionAsync POST /api/criminalprosecution/validate
EPA LookupEpaProsecutionAsync POST /api/criminalprosecution/lookup
Healthcare CheckHealthcareExclusionAsync POST /api/providerexclusion/validate
Healthcare LookupHealthcareExclusionAsync POST /api/providerexclusion/lookup
Healthcare LookupHealthcareAsync POST /api/healthcare/lookup
Risk CheckBankruptcyRiskAsync POST /api/risk/riskcontrol (Bankruptcy)
Risk LookupCreditScoreAsync POST /api/risk/riskcontrol (Credit Score)
Risk LookupFailRateAsync POST /api/risk/riskcontrol (Fail Rate)
Risk AssessEntityRiskAsync POST /api/entity/fraud/lookup
Risk LookupCreditAnalysisAsync POST /api/creditanalysis/lookup
ESG LookupEsgScoreAsync POST /api/esg/Scores
Cybersecurity DomainSecurityReportAsync POST /api/itsecurity/domainreport
Cybersecurity CheckIpQualityAsync POST /api/ipquality/validate
Ownership LookupBeneficialOwnershipAsync POST /api/beneficialownership/lookup
Hierarchy LookupCorporateHierarchyAsync POST /api/corporatehierarchy/lookup
Hierarchy LookupHierarchyAsync POST /api/company/hierarchy/lookup
DUNS LookupDunsAsync POST /api/duns-number-lookup
Healthcare ValidateNpiAsync POST /api/nationalprovideridentifier/validate
Healthcare ValidateMedpassAsync POST /api/medpass/validate
DOT LookupDotCarrierAsync POST /api/dot/fmcsa/lookup
Identity ValidateIndiaIdentityAsync POST /api/inidentity/validate
Certification ValidateCertificationAsync POST /api/certification/validate
Certification LookupCertificationAsync POST /api/certification/lookup
Classification LookupBusinessClassificationAsync POST /api/businessclassification/lookup
Classification GetNaicsCodesAsync GET /api/businessclassification/naics/{digits}
Classification GetSicCodesAsync GET /api/businessclassification/sic/{digits}
Payment Terms AnalyzePaymentTermsAsync POST /api/paymentterms/validate
SAP Ariba LookupAribaSupplierAsync POST /api/aribasupplierprofile/lookup
SAP Ariba ValidateAribaSupplierAsync POST /api/aribasupplierprofile/validate
Gender IdentifyGenderAsync POST /api/genderize/identifygender
Bulk CheckBulkStatusAsync POST /api/bulkstatus/check

Marked obsolete (still callable)

Method Reason
LookupExchangeRatesAsync Endpoint marked internal (ApiExplorerSettings(IgnoreApi=true)) — may change without notice.
ScreenContinuousAsync Endpoint is currently a stub returning HTTP 501.

Examples

Tax validation

var tax = await client.ValidateTaxAsync(new TaxRequest
{
    IdentityNumber = "12-3456789",
    IdentityNumberType = "EIN",
    Country = "US",
    EntityName = "Acme Inc.",
});

Console.WriteLine($"Identity: {tax.IdentityNumber}, Authority record: {tax.TaxAdditionalInfo?.EntityNameFromTaxAuthorityRecord}");

Sanctions screening (returns one record per matched provider)

var matches = await client.CheckSanctionsAsync(new SanctionsRequest
{
    CompanyName = "Acme Trading Ltd",
    Country = "US",
});

foreach (var m in matches)
{
    if (m.IsMatch)
        Console.WriteLine($"MATCH (score: {m.Score}) — {m.Description}");
}

PEP screening

using System;
using System.Collections.Generic;

var pep = await client.ScreenPepAsync(new PepRequest { Name = "John Doe", Country = "US" });
foreach (var entry in pep)
{
    foreach (var person in entry.Persons ?? Array.Empty<PepPerson>())
    {
        Console.WriteLine($"{person.Name} (score: {entry.Score})");
    }
    // Embedded RCAs (premium PEP catalog clients only) — relatives & close associates.
    foreach (var rca in entry.RelativesAndAssociates ?? Array.Empty<RcaPersonResponse>())
    {
        Console.WriteLine($"  RCA: {rca.Name} ({rca.RelationshipType}, {rca.RelationshipStatus})");
    }
}

Disqualified directors (person-keyed)

var dq = await client.CheckDirectorsAsync(new DirectorsRequest
{
    FirstName = "John",
    LastName  = "Doe",
    Country   = "GB",
});

Business registration

The endpoint returns a list of registration records — iterate the response directly.

var br = await client.LookupBusinessRegistrationAsync(new BusinessRegistrationRequest
{
    EntityName = "Apple Inc.",
    Country    = "US",
    State      = "CA",
});

foreach (var reg in br)
{
    foreach (var match in reg.BusinessRegistrations ?? Array.Empty<BusinessRegistration>())
    {
        Console.WriteLine($"{match.RegistrationId}: {match.EntityName} ({match.Status})");
    }
}

Tax format validation

var fmt = await client.ValidateTaxFormatAsync(new TaxFormatRequest
{
    CountryIso2        = "US",
    IdentityNumberType = "EIN",
    IdentityNumber     = "12-3456789",
});

Risk control (Bankruptcy / Credit Score / Fail Rate)

These endpoints return a single RiskControlResponse (not a list).

var bk = await client.CheckBankruptcyRiskAsync(new BankruptcyRequest
{
    CompanyName = "Acme Corp",
    Country     = "US",
});

Console.WriteLine($"{bk.Category}{bk.Recommendation} (cases: {bk.Cases?.Count ?? 0})");

Healthcare lookup (extended)

LookupHealthcareAsync requires an 18-character NetworkEntityId.

var hc = await client.LookupHealthcareAsync(new HealthcareLookupRequest
{
    HealthCareType  = "HCO",
    NetworkEntityId = "AAAAAAAAAAAAAAAAAA",
});

Bank Pro (premium analytics)

var pro = await client.ValidateBankProAsync(new BankProRequest
{
    AccountNumber  = "1234567890",
    BankNumberType = "ACCOUNT",
    Country        = "US",
    BankCode       = "021000021",
    SwiftCode      = "CHASUS33",
});
Console.WriteLine($"Match score: {pro.Score}, code: {pro.MatchCode}");
foreach (var flag in pro.BankProValidations?.RedFlagReasons ?? Array.Empty<string>())
    Console.WriteLine($"  red flag: {flag}");

Bank validation (with CBU for Argentina)

BankNumberType is the discriminator the server uses to route the request — set it to IBAN, ACCOUNT, CLABE, CBU, etc. Without it the server falls back to inference which can produce a 400 for ambiguous inputs.

var bank = await client.ValidateBankAccountAsync(new BankRequest
{
    BusinessEntityType = "Corporation",
    Country            = "AR",
    BankAccountHolder  = "Acme SA",
    BankNumberType     = "CBU",
    CBU                = "0170001540000001234567",
});

Bulk status

var status = await client.CheckBulkStatusAsync(new BulkStatusRequest
{
    CallBackId = Guid.Parse("6611e45f-f625-421b-aa72-a11925920aef"),
});
Console.WriteLine($"{status.Status}: {status.ProcessedRecords}/{status.TotalRecords}");

Company hierarchy (parent / children)

var h = await client.LookupHierarchyAsync(new HierarchyRequest
{
    Identifier = "123456789",
    IdentifierType = "DUNS",
    Country = "US",
});

void Print(HierarchyCompany? c, int depth = 0)
{
    if (c is null) return;
    Console.WriteLine(new string(' ', depth * 2) + c.CompanyName);
    foreach (var child in c.Children ?? Array.Empty<HierarchyCompany>()) Print(child, depth + 1);
}
Print(h.Parent);

Corporate hierarchy (US only)

var ch = await client.LookupCorporateHierarchyAsync(new CorporateHierarchyRequest
{
    CompanyName = "Apple Inc.",
    AddressLine1 = "1 Apple Park Way",
    City = "Cupertino",
    State = "CA",
    ZipCode = "95014",
});
foreach (var entry in ch)
{
    Console.WriteLine($"Transaction: {entry.ELSGenericMessage?.TransactionId}");
}

DUNS lookup

var duns = await client.LookupDunsAsync(new DunsRequest { DunsNumber = "123456789" });
foreach (var d in duns)
{
    // Per-supplier detail items are returned in provider-shape (opaque). Drill in
    // via the JsonElement dictionary.
    foreach (var item in d.DnbDirectSupplierDetailItems ?? Array.Empty<IReadOnlyDictionary<string, System.Text.Json.JsonElement>>())
    {
        if (item.TryGetValue("companyName", out var cn))
            Console.WriteLine($"DUNS hit → {cn}");
    }
}

Medpass (healthcare supplier)

var mp = await client.ValidateMedpassAsync(new MedpassRequest
{
    ID                 = "1234567890",
    BusinessEntityType = "Corporation",
    CompanyName        = "Acme Health Inc.",
});
Console.WriteLine($"Status: {mp.Status} (last updated {mp.LastUpdatedDate:O})");

Error Handling

All errors thrown by the SDK derive from QubitonException.

try
{
    var resp = await client.ValidateAddressAsync(req);
}
catch (QubitonRateLimitException ex)
{
    // 429 — back off using ex.RetryAfter
}
catch (QubitonAuthException ex)
{
    // 401 / 403
}
catch (QubitonTimeoutException ex)
{
    // Per-request timeout fired (Options.Timeout). The inner exception is the
    // underlying OperationCanceledException for diagnostics. Distinct from
    // OperationCanceledException raised by caller-supplied CancellationToken cancellation.
}
catch (QubitonValidationException ex)
{
    // 4xx (other than auth/404/429)
}
catch (QubitonNotFoundException)
{
    // 404
}
catch (QubitonServerException ex)
{
    // 5xx or transport
}

Note: QubitonTimeoutException derives from QubitonException (status code 0) — placing its catch above QubitonServerException ensures it's matched first; otherwise the more general server catch would fire on transport-level failures.

The SDK retries 408, 429 and 5xx automatically (up to MaxRetries, default 3) with exponential backoff (base * 2^attempt) plus 0–25% jitter ABOVE the floor (so the actual delay is always at least the base / Retry-After value), capped at 30 seconds. The Retry-After header is honored as a floor.

A 401 from the server when OAuth is configured triggers a single transparent token refresh + retry — that retry does not consume one of your MaxRetries attempts.

Configuration

var client = new QubitOnClient(new QubitOnClientOptions
{
    ApiKey            = "svm...",
    BaseUrl           = "https://api.qubiton.com",      // default
    Timeout           = TimeSpan.FromSeconds(30),       // default
    MaxRetries        = 3,                              // default; clamped to >=1
    MaxBackoff        = TimeSpan.FromSeconds(30),       // default; clamped to <=30s
    RequestedByClient = "my-app/1.2.3",                 // optional override; defaults to SDK UA
    OAuth2TokenSkew   = TimeSpan.FromSeconds(30),       // default; clamped to [0s, 5min]
    MaxConnectionsPerServer = 100,                       // default; net6+ only
    IdempotencyKey    = null,                            // optional X-Idempotency-Key header
});

MaxRetries is the total attempt count (initial + retries). A value of 1 disables retries. MaxBackoff is clamped at 30 seconds; values above 30 seconds are silently reduced. Setting RequestedByClient overrides the value injected into request bodies for the server-required RequestedByClient field; if unset, the SDK uses its own user-agent string. RequestedByClient is truncated at 350 characters (server [MaxLength(350)]). The SDK does not retry HTTP 501 responses (the upstream is explicitly telling us the endpoint isn't implemented).

MCP Protocol Support

This API is also available as a native Model Context Protocol (MCP) server.

Getting an API Key

  1. Sign up at www.qubiton.com.
  2. Navigate to Dashboard → API Keys.
  3. Copy your API key (starts with svm).

License

MIT

About

Official .NET SDK for the QubitOn API — validate addresses, tax IDs, bank accounts, phones, emails; look up business registrations; screen against sanctions, PEP, and disqualified-director lists; assess credit, ESG, and entity risk.

Resources

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages