Backwork

.NET SDK

Official Backwork SDK for C# and .NET with async/await support

The official Backwork .NET SDK provides a typed async client for Medicare and commercial coverage intelligence, medical code lookup, prior authorization checks, claim validation, policy search, spending data, and formulary evidence.

Installation

dotnet add package Backwork.SDK --version 2.0.0

Requirements: .NET Standard 2.0+ (.NET Framework 4.6.1+, .NET Core 2.0+, .NET 5+)

Quick Start

using Backwork.SDK;

using var client = new BackworkClient("bwk_live_YOUR_API_KEY");

var health = await client.HealthAsync();
Console.WriteLine(health.Data?.Status);

Get an API key from the Developer Console.

Authentication

Pass the API key directly, or read it from configuration or an environment variable.

using Backwork.SDK;

var apiKey = Environment.GetEnvironmentVariable("BACKWORK_API_KEY")
    ?? throw new InvalidOperationException("BACKWORK_API_KEY is required");

using var client = new BackworkClient(apiKey);

The default base URL is https://backworkhealth.com/api/v1. You can override it for staging or local testing:

using var client = new BackworkClient(
    apiKey,
    baseUrl: "https://backworkhealth.com/api/v1"
);

Common Workflows

Health Check

var health = await client.HealthAsync();

Console.WriteLine($"{health.Data?.Status} {health.Data?.Version}");

Look Up a Medical Code

var result = await client.LookupCodeAsync(
    code: "76942",
    jurisdiction: "JM",
    include: new[] { "rvu", "policies" }
);

var code = result.Data;
Console.WriteLine($"{code?.Code}: {code?.Description}");
Console.WriteLine($"Facility price: ${code?.Rvu?.FacilityPrice}");
Console.WriteLine($"Policies: {code?.Policies?.Count ?? 0}");

Batch Code Lookup

var result = await client.BatchLookupCodesAsync(
    codes: new[] { "76942", "27447", "NOPE999" },
    include: new[] { "rvu", "policies" }
);

foreach (var item in result.Data?.Results ?? new())
{
    Console.WriteLine($"{item.Key}: found={item.Value.Found}");
}

NOPE999 returns an item with found=false instead of failing the whole batch.

Search Policies

var policies = await client.ListPoliciesAsync(
    query: "ultrasound guidance",
    policyType: "LCD",
    status: "active",
    limit: 10
);

foreach (var policy in policies.Data ?? new())
{
    Console.WriteLine($"{policy.PolicyId}: {policy.Title}");
}

Get a Policy

var policy = await client.GetPolicyAsync(
    "L33831",
    include: new[] { "criteria", "codes" }
);

Console.WriteLine(policy.Data?.Title);
Console.WriteLine(policy.Data?.PdfUrl);

Check Prior Authorization

var result = await client.CheckPriorAuthAsync(
    procedureCodes: new[] { "27447" },
    diagnosisCodes: new[] { "M17.11" },
    state: "TX",
    payer: "medicare"
);

Console.WriteLine($"PA required: {result.Data?.PaRequired}");
Console.WriteLine($"Confidence: {result.Data?.Confidence}");
Console.WriteLine($"Reason: {result.Data?.Reason}");

foreach (var item in result.Data?.DocumentationChecklist ?? new())
{
    Console.WriteLine($"- {item}");
}

Validate Claim Risk

var claim = await client.ValidateClaimWithDateOfServiceAsync(
    procedureCodes: new[] { "27447" },
    diagnosisCodes: new[] { "M17.11" },
    payer: "Medicare",
    state: "TX",
    dateOfService: "2026-05-23"
);

Console.WriteLine($"Coverage: {claim.Data?.CoverageStatus}");
Console.WriteLine($"Prior auth: {claim.Data?.PriorAuthRequired}");
Console.WriteLine($"Denial risk: {claim.Data?.DenialRisk}");

Monitor Policy Changes

var since = DateTime.UtcNow.AddDays(-7).ToString("yyyy-MM-ddTHH:mm:ssZ");

var changes = await client.GetPolicyChangesAsync(
    since: since,
    limit: 20
);

foreach (var change in changes.Data ?? new())
{
    Console.WriteLine($"{change.PolicyId}: {change.ChangeType}");
    Console.WriteLine(change.ChangeSummary);
}

Search Coverage Criteria

var criteria = await client.SearchCriteriaAsync(
    query: "BMI greater than 40",
    section: "indications",
    policyType: "LCD",
    limit: 10
);

foreach (var block in criteria.Data ?? new())
{
    Console.WriteLine($"{block.PolicyId ?? block.Policy?.PolicyId}: {block.Text}");
}

List Jurisdictions

var jurisdictions = await client.ListJurisdictionsAsync();

foreach (var jurisdiction in jurisdictions.Data ?? new())
{
    Console.WriteLine($"{jurisdiction.JurisdictionCode}: {jurisdiction.MacName}");
}

Compare Policies

var comparison = await client.ComparePoliciesAsync(
    procedureCodes: new[] { "76942" },
    jurisdictions: new[] { "J05", "J06", "JM" }
);

Console.WriteLine(comparison.Data);

Medicaid Spending

var result = await client.GetSpendingByCodeAsync(
    code: "T1019",
    year: 2023
);

if (result.Data?.TryGetValue("T1019", out var spending) == true)
{
    Console.WriteLine($"Total paid: ${spending.TotalPaid}");
    Console.WriteLine($"Total claims: {spending.TotalClaims}");
}

Drug Formulary Evidence

var formulary = await client.SearchDrugFormularyEvidenceAsync(
    query: "metformin",
    payer: "all",
    limit: 5
);

foreach (var item in formulary.Data ?? new())
{
    Console.WriteLine($"{item.Source}: {item.DrugName} {item.CoverageStatus}");
}

Webhooks and Compliance

Webhook and compliance write operations require API keys with the right plan and scope. If the key does not have access, the SDK throws a BackworkException with the API status code and error code.

try
{
    var webhooks = await client.ListWebhooksAsync();
    Console.WriteLine(webhooks.Data?.Count ?? 0);
}
catch (BackworkException ex) when (ex.StatusCode == 403)
{
    Console.WriteLine($"Scope or plan required: {ex.Code}");
}

ASP.NET Core

Register BackworkClient as a singleton when your application uses one API key for outbound Backwork calls.

using Backwork.SDK;

builder.Services.AddSingleton(sp =>
{
    var apiKey = builder.Configuration["Backwork:ApiKey"]
        ?? throw new InvalidOperationException("Backwork:ApiKey is required");

    return new BackworkClient(apiKey);
});

Then inject it into controllers or services:

using Microsoft.AspNetCore.Mvc;
using Backwork.SDK;

[ApiController]
[Route("api/codes")]
public class CodesController : ControllerBase
{
    private readonly BackworkClient _backwork;

    public CodesController(BackworkClient backwork)
    {
        _backwork = backwork;
    }

    [HttpGet("{code}")]
    public async Task<IActionResult> GetCode(string code)
    {
        var result = await _backwork.LookupCodeAsync(
            code,
            include: new[] { "rvu", "policies" }
        );

        return Ok(result);
    }
}

Error Handling

using Backwork.SDK;

try
{
    var result = await client.LookupCodeAsync("76942");
}
catch (BackworkException ex) when (ex.StatusCode == 401)
{
    Console.WriteLine($"Authentication failed: {ex.Code}");
}
catch (BackworkException ex) when (ex.StatusCode == 429)
{
    Console.WriteLine("Rate limited. Retry with backoff.");
}
catch (BackworkException ex)
{
    Console.WriteLine($"Backwork API error {ex.StatusCode} ({ex.Code}): {ex.Message}");
}

Response Format

All methods return ApiResponse<T>:

public class ApiResponse<T>
{
    public bool Success { get; set; }
    public T? Data { get; set; }
    public object? Meta { get; set; }
}

Error responses are raised as BackworkException.

Resources

Next Steps

Last updated on

On this page