Whoop.Sdk.Extensions.DependencyInjection 2.0.0

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

Whoop.Sdk

Whoop.Sdk Whoop.Sdk downloads Whoop.Sdk.Extensions.DependencyInjection Whoop.Sdk.Extensions.DependencyInjection downloads

ci targets License: MIT

A dependency-light .NET client for the WHOOP v2 Developer API, generated by hand from the published OpenAPI document at https://api.prod.whoop.com/developer/doc/openapi.json.

  • Targets netstandard2.0, netstandard2.1, net8.0 and net10.0 — usable from .NET Framework 4.6.2+, .NET Core, .NET 5 through .NET 10, Xamarin, MAUI, Unity and Mono.
  • Complete v2 surface: cycles, sleep, recovery, workouts, user profile and body measurements, the v1 activity ID mapping, and the trusted-partner lab endpoints.
  • OAuth2 built in: authorization-code URL building, code exchange, automatic refresh with rotation, and the partner client-credentials flow.
  • Cursor pagination as IAsyncEnumerable<T> so you can await foreach a whole date range.
  • No dependencies at all on net8.0 and net10.0System.Text.Json and IAsyncEnumerable<T> are in-box there. The .NET Standard targets add System.Text.Json and Microsoft.Bcl.AsyncInterfaces, and nothing else.

Target frameworks

Target Dependencies Notes
net10.0 none LTS, supported through 10 Nov 2028. Exact-match asset for current apps.
net8.0 none LTS, supported through 10 Nov 2026.
netstandard2.1 System.Text.Json, Microsoft.Bcl.AsyncInterfaces .NET Core 3.x, Xamarin, Unity.
netstandard2.0 System.Text.Json, Microsoft.Bcl.AsyncInterfaces .NET Framework 4.6.2+, Mono.

There is no dedicated net9.0 asset and none is needed: a net9.0 app resolves the net8.0 one, still dependency-free. .NET 9 was STS and reached end of support on 12 May 2026.

The Microsoft.Extensions.* versions in the DI package are a floor, not a pin: an app already on a newer patch or major resolves upward.

Packages

Package Purpose Latest
Whoop.Sdk The client itself. Whoop.Sdk
Whoop.Sdk.Extensions.DependencyInjection IServiceCollection / IHttpClientFactory wiring. Whoop.Sdk.Extensions.DependencyInjection
dotnet add package Whoop.Sdk
dotnet add package Whoop.Sdk.Extensions.DependencyInjection   # only if you want the DI wiring

Quick start

using Whoop.Sdk;
using Whoop.Sdk.Models;

using var whoop = new WhoopClient("<access-token>");

var profile = await whoop.User.GetBasicProfileAsync();
Console.WriteLine($"{profile.FirstName} {profile.LastName}");

var recovery = await whoop.Recovery.ListAsync(new WhoopCollectionRequest { Limit = 10 });
foreach (var record in recovery.Records)
{
    Console.WriteLine($"{record.CreatedAt:d}: {record.Score?.RecoveryScorePercentage}%");
}

Streaming a date range

EnumerateAsync follows the next_token cursor for you and only fetches a page when you consume one.

var lastMonth = new WhoopCollectionRequest
{
    Start = DateTimeOffset.UtcNow.AddDays(-30),
    Limit = 25,
};

await foreach (var workout in whoop.Workouts.EnumerateAsync(lastMonth))
{
    Console.WriteLine($"{workout.SportName} - strain {workout.Score?.Strain:F1}");
}

Authentication

Authorization code flow

using Whoop.Sdk.Authentication;

using var http = new HttpClient();
var oauth = new WhoopOAuthClient(http, "<client-id>", "<client-secret>");

// 1. Send the user here. WHOOP requires a state value of at least eight characters.
var consentUrl = oauth.CreateAuthorizationUrl(
    new Uri("https://example.com/callback"),
    new[] { WhoopScopes.ReadProfile, WhoopScopes.ReadRecovery, WhoopScopes.Offline },
    state: Guid.NewGuid().ToString("N"));

// 2. Exchange the code from the redirect.
var token = await oauth.ExchangeAuthorizationCodeAsync(code, new Uri("https://example.com/callback"));

Keeping tokens fresh

RefreshingWhoopTokenProvider hands out the cached access token until it is about to expire, refreshes exactly once even under concurrency, and calls you back so you can persist the rotated refresh token — WHOOP invalidates the previous one on every refresh.

var tokens = new RefreshingWhoopTokenProvider(
    oauth,
    refreshToken: storedRefreshToken,
    initialToken: token,
    onTokenRefreshed: async (refreshed, ct) => await store.SaveAsync(refreshed.RefreshToken!, ct));

using var whoop = new WhoopClient(new WhoopClientOptions { TokenProvider = tokens });

Trusted partners

using var tokenTransport = new HttpClient { BaseAddress = WhoopClientOptions.DefaultBaseAddress };
var partnerTokens = new PartnerWhoopTokenProvider(tokenTransport, "<client-id>", "<client-secret>");

using var whoop = new WhoopClient(new WhoopClientOptions { TokenProvider = partnerTokens });
var requisition = await whoop.Partner.GetLabRequisitionAsync(requisitionId);

Dependency injection

using Whoop.Sdk.Extensions.DependencyInjection;

builder.Services.AddWhoopAccessToken(token);           // or register your own IWhoopTokenProvider
builder.Services.AddWhoopClient()                      // typed HttpClient + auth handler
    .AddStandardResilienceHandler();                   // any IHttpClientBuilder extension works

For trusted partners, AddWhoopPartnerAuthentication registers a token provider that acquires tokens over a separate unauthenticated pipeline, so token acquisition cannot recurse through the auth handler.

Samples

Four runnable projects live in samples/ — see samples/README.md for how to run each.

Sample Shows
Whoop.Sdk.Samples.QuickStart One access token, no DI: profile, today's cycle, recent recovery, workout totals.
Whoop.Sdk.Samples.Worker Generic Host, IWhoopClient in a BackgroundService, static token or auto-refresh, resilience handler.
Whoop.Sdk.Samples.OAuthWebApp Full authorization-code flow with a per-request scoped token provider.
Whoop.Sdk.Samples.TrustedPartner Client-credentials partner auth, lab requisitions and result upload.

Error handling

Every non-success response becomes a WhoopApiException carrying the status code, the raw body, and the request URI. HTTP 429 becomes a WhoopRateLimitExceededException exposing RetryAfter.

try
{
    var cycle = await whoop.Cycles.GetAsync(cycleId);
}
catch (WhoopRateLimitExceededException ex)
{
    await Task.Delay(ex.RetryAfter ?? TimeSpan.FromSeconds(30));
}
catch (WhoopApiException ex) when (ex.IsNotFound)
{
    // no such cycle
}

WHOOP's documented limits are 100 requests per minute and 10,000 per day per user. Combine AddWhoopClient() with Microsoft.Extensions.Http.Resilience if you want automatic retries.

Scores may be absent

Cycle, Sleep, Recovery and Workout all carry a ScoreState. Only ScoreState.Scored guarantees a populated Score; PendingScore means "retry later" and Unscorable means it will never arrive. Unrecognised future values deserialize to ScoreState.Unknown rather than throwing.

API coverage

Endpoint Member
GET /v2/cycle Cycles.ListAsync / Cycles.EnumerateAsync
GET /v2/cycle/{cycleId} Cycles.GetAsync
GET /v2/cycle/{cycleId}/sleep Cycles.GetSleepAsync, Sleep.GetForCycleAsync
GET /v2/cycle/{cycleId}/recovery Cycles.GetRecoveryAsync, Recovery.GetForCycleAsync
GET /v2/recovery Recovery.ListAsync / Recovery.EnumerateAsync
GET /v2/activity/sleep Sleep.ListAsync / Sleep.EnumerateAsync
GET /v2/activity/sleep/{sleepId} Sleep.GetAsync
GET /v2/activity/workout Workouts.ListAsync / Workouts.EnumerateAsync
GET /v2/activity/workout/{workoutId} Workouts.GetAsync
GET /v2/user/profile/basic User.GetBasicProfileAsync
GET /v2/user/measurement/body User.GetBodyMeasurementAsync
DELETE /v2/user/access User.RevokeAccessAsync
GET /v1/activity-mapping/{activityV1Id} ActivityMappings.GetAsync
POST /v2/partner/token Partner.RequestTokenAsync
GET /v2/partner/requisition/{id} Partner.GetLabRequisitionAsync
PATCH /v2/partner/requisition/{id}/status Partner.UpdateLabRequisitionStatusAsync
GET /v2/partner/service-request/{id} Partner.GetServiceRequestAsync
PATCH /v2/partner/service-request/{id}/status Partner.UpdateServiceRequestStatusAsync
POST /v2/partner/service-request/{id}/results Partner.UploadDiagnosticReportResultsAsync
POST /v2/partner/development/add-test-data Partner.AddTestDataAsync

Anything not wrapped yet is reachable through WhoopClient.Connection, which exposes the raw typed SendAsync used by every endpoint client.

Building

dotnet build Whoop.Sdk.slnx
dotnet test Whoop.Sdk.slnx
dotnet pack Whoop.Sdk.slnx -c Release   # packages land in artifacts/packages

The vendored copy of the spec this client was written against lives in assets/whoop-openapi.json.

License

MIT. Not affiliated with or endorsed by WHOOP, Inc.

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 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 is compatible.  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 is compatible. 
.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
2.0.0 85 8/23/2026
1.0.0 83 8/22/2026