MvService.Cbm.ExternalClient 1.0.0

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

MvService.Cbm.ExternalClient

.NET client library for the Contract Billing Management (CBM) External API v1.

Covers:

  • Contracts (read + line-item write)
  • On-demand billing items (generic + typed time/material/usage ingress)
  • Asset bulk-import
  • ERP order event ingestion
  • Customer management (create, read, update, lookup by external ref)
  • Subscription lifecycle (trial, plan change, cancellation, status)

Installation

<PackageReference Include="MvService.Cbm.ExternalClient" Version="1.*" />

Add the GitHub Packages source to your nuget.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="github-mv-service"
         value="https://nuget.pkg.github.com/MV-Service/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <github-mv-service>
      <add key="Username" value="%GITHUB_USERNAME%" />
      <add key="ClearTextPassword" value="%GITHUB_TOKEN%" />
    </github-mv-service>
  </packageSourceCredentials>
</configuration>

GITHUB_TOKEN needs read:packages scope (classic PAT or fine-grained with read access to the MV-Service org packages).

Quickstart

X-Api-Key authentication (default)

builder.Services
    .AddCbmExternalClient(o =>
    {
        o.BaseUrl = "https://cbm.example.com";
        o.ApiKey  = "your-api-key";
    })
    .AddStandardResilienceHandler(); // optional — requires Microsoft.Extensions.Http.Resilience

OAuth 2.0 client_credentials flow

builder.Services
    .AddCbmExternalClient(o =>
    {
        o.BaseUrl      = "https://cbm.example.com";
        o.UseOAuthFlow = true;
        o.ClientId     = "your-marketplace-app-id";
        o.ClientSecret = "your-client-secret";
        o.Scope        = "contracts:read billing:write assets:write";
    });

Tokens are cached and refreshed automatically (30-second buffer before expiry).

Basic usage

public class MyService(ICbmExternalClient cbm)
{
    public async Task RunAsync(CancellationToken ct)
    {
        // List active contracts
        var page = await cbm.ListContractsAsync(
            status: CbmContractStatus.Active, ct: ct);

        foreach (var contract in page.Items)
            Console.WriteLine($"{contract.ContractNumber} — {contract.CustomerName}");

        // Submit a time entry
        var entry = await cbm.SubmitTimeEntryAsync(new CbmTimeEntryRequest
        {
            CustomerExternalRef = "CUST-001",
            Hours               = 2.5m,
            OccurredAt          = DateTime.UtcNow,
            Description         = "Maintenance work",
        }, ct);

        Console.WriteLine($"Recorded: {entry.Id}");

        // Bulk-import assets
        var result = await cbm.ImportAssetsAsync(new CbmBulkImportRequest
        {
            ThirdPartyAppId = Guid.Parse("..."),
            Assets =
            [
                new CbmBulkImportAssetItem
                {
                    ExternalAssetId    = "SRV-001",
                    Name               = "Web Server 01",
                    AssetType          = "Server",
                    ExternalReferenceUrl = "https://idoit.example.com/cmdb/1234",
                },
            ],
        }, ct);

        Console.WriteLine($"Imported: {result.Created} created, {result.Updated} updated");
    }
}

API Reference

Method Description
GetTokenAsync Fetch OAuth token (client_credentials)
ListContractsAsync Paged contract list with optional status/customer filter
GetContractAsync Single contract by ID
GetLineItemsAsync All line items for a contract
CreateLineItemAsync Add a line item to a contract
GetOnDemandItemsAsync On-demand items for a billing run
AddOnDemandItemAsync Add a generic on-demand item to a billing run
SubmitTimeEntryAsync Typed time-entry ingress (auto-matched to contract)
SubmitMaterialEntryAsync Typed material-entry ingress
SubmitUsageEntryAsync Typed usage/consumption entry ingress
SendErpOrderEventAsync Notify CBM of a new ERP order (may auto-create contract)
ImportAssetsAsync Bulk-import assets from a third-party system
RegisterWebhookAsync Register a webhook subscription
ListWebhooksAsync List all webhook subscriptions
UpdateWebhookAsync Update an existing webhook subscription
DeleteWebhookAsync Delete a webhook subscription
Customers
CreateCustomerAsync Create a new customer
GetCustomerAsync Get a customer by ID (returns null if not found)
FindCustomerByExternalRefAsync Find a customer by external reference (returns null if not found)
UpdateCustomerAsync Update an existing customer
Subscriptions
StartTrialAsync Start a trial subscription for a customer
ChangePlanAsync Upgrade or downgrade a customer's subscription plan
CancelSubscriptionAsync Cancel a customer's subscription (optionally at period end)
GetSubscriptionAsync Get a customer's current subscription status (returns null if none)

Webhook Event Types

Use the CbmWebhookEventTypes constants class when registering webhook subscriptions:

await cbm.RegisterWebhookAsync(new CbmCreateWebhookRequest
{
    Url = "https://my-app.example.com/webhooks/cbm",
    EventTypes =
    [
        CbmWebhookEventTypes.CustomerCreated,
        CbmWebhookEventTypes.SubscriptionTrialStarted,
        CbmWebhookEventTypes.SubscriptionUpgraded,
        CbmWebhookEventTypes.SubscriptionCancelled,
    ],
});

Available event types:

Constant Value
CustomerCreated customer.created
CustomerUpdated customer.updated
ContractCreated contract.created
ContractUpdated contract.updated
ContractCancelled contract.cancelled
ContractAccepted contract.accepted
BillingRunCompleted billing_run.completed
BillingRunItemAdded billingrun.item_added
BillingRunAboutToClose billing_run.about_to_close
SubscriptionTrialStarted subscription.trial_started
SubscriptionUpgraded subscription.upgraded
SubscriptionDowngraded subscription.downgraded
SubscriptionCancelled subscription.cancelled
SubscriptionTrialExpiring subscription.trial_expiring
SubscriptionTrialExpired subscription.trial_expired

Error handling

All HTTP errors surface as typed exceptions:

Exception HTTP status
CbmUnauthorizedException 401
CbmForbiddenException 403
CbmNotFoundException 404
CbmRateLimitException 429 — includes RetryAfter timespan
CbmClientException Any other non-2xx

All exceptions expose StatusCode and ResponseBody for diagnostics.

Resilience

AddCbmExternalClient returns an IHttpClientBuilder. Chain .AddStandardResilienceHandler() (from Microsoft.Extensions.Http.Resilience) or any Polly pipeline you need:

builder.Services
    .AddCbmExternalClient(o => { ... })
    .AddResilienceHandler("cbm", pipeline =>
    {
        pipeline.AddRetry(new HttpRetryStrategyOptions { MaxRetryAttempts = 3 });
        pipeline.AddCircuitBreaker(new HttpCircuitBreakerStrategyOptions());
    });

License

UNLICENSED — internal MV-Service component.

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 is compatible.  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.0.0 128 5/31/2026
0.1.4 122 5/27/2026