GarminRunerz.Connect
1.2.0
dotnet add package GarminRunerz.Connect --version 1.2.0
NuGet\Install-Package GarminRunerz.Connect -Version 1.2.0
<PackageReference Include="GarminRunerz.Connect" Version="1.2.0" />
<PackageVersion Include="GarminRunerz.Connect" Version="1.2.0" />
<PackageReference Include="GarminRunerz.Connect" />
paket add GarminRunerz.Connect --version 1.2.0
#r "nuget: GarminRunerz.Connect, 1.2.0"
#:package GarminRunerz.Connect@1.2.0
#addin nuget:?package=GarminRunerz.Connect&version=1.2.0
#tool nuget:?package=GarminRunerz.Connect&version=1.2.0
GarminRunerz.Connect
Uploads workouts built with GarminRunerz.Workout.Models / GarminRunerz.Workout.Services to a Garmin Connect account.
Authentication (Garmin SSO, MFA, token caching) is handled by the unofficial Unofficial.Garmin.Connect client; workout creation posts your serialized workout JSON directly to Garmin Connect's workout-service API — the same API the Garmin Connect web app uses.
⚠️ This relies on Garmin Connect's private web API. It is intended for personal automation with your own account. It may break without notice if Garmin changes their API, and heavy or multi-account use may violate Garmin's terms of service.
Installation
dotnet add package GarminRunerz.Connect
Usage
Registration
services.AddGarminConnectUploader(options =>
{
options.Email = Environment.GetEnvironmentVariable("GARMIN_EMAIL")!;
options.Password = Environment.GetEnvironmentVariable("GARMIN_PASSWORD")!;
options.TokenCacheFilePath = "garmin-tokens.json"; // optional: persist tokens between runs
});
Uploading a workout
var uploader = provider.GetRequiredService<IWorkoutUploadService>();
// Build the workout with GarminRunerz.Workout.Services, then:
var created = await uploader.CreateWorkoutAsync(workout);
Console.WriteLine($"Created workout {created.WorkoutId}");
// Optionally schedule it on the Garmin Connect calendar:
await uploader.ScheduleWorkoutAsync(created.WorkoutId!.Value, new DateOnly(2026, 8, 20));
Once created (and synced), the workout appears in Garmin Connect and on your watch.
MFA accounts
If your account uses multi-factor authentication, provide an IMfaCodeProvider:
options.MfaCodeProvider = new StaticMfaCode("123456");
// or implement IMfaCodeProvider to prompt/fetch the code dynamically
With TokenCacheFilePath set, the MFA prompt only happens on the first run — subsequent runs reuse the cached OAuth tokens.
Token-based upload (no password)
If you already hold a Garmin OAuth2 access token — for example one minted from a stored long-lived
OAuth1 token via GarminRunerz.Connect.Auth —
you can upload without a password or a GarminConnectContext. Requests go straight to
connectapi.garmin.com carrying only Authorization: Bearer <token> and the di-backend header.
// token is a GarminOAuth2Token obtained from GarminRunerz.Connect.Auth
// (e.g. authClient.RefreshOAuth2Async(storedOAuth1Token)).
services.AddGarminConnectTokenUploader(token);
// then, exactly as with the password path:
var uploader = provider.GetRequiredService<IWorkoutUploadService>();
var created = await uploader.CreateWorkoutAsync(workout);
await uploader.ScheduleWorkoutAsync(created.WorkoutId!.Value, new DateOnly(2026, 8, 20));
For long-running hosts where the ~1-hour token expires, register your own
IGarminAccessTokenProvider (which refreshes from the stored OAuth1 token) and call
AddGarminConnectTokenUploader() with no arguments — the provider is resolved from the container.
Both the token path and the password path implement the same IWorkoutUploadService.
Multi-user hosting: IGarminConnectionManager
For a hosted app serving several users, IGarminConnectionManager is the one-call layer that ties
everything together per user key. Given a user key it loads that user's stored OAuth1 token, mints
(and caches until near expiry) an OAuth2 access token, uploads through the token path, and — if
Garmin rejects the token with a 401 — refreshes once and retries.
services.AddGarminConnectionManager();
// Register your own encrypted IOAuth1TokenStore before this call for durable storage;
// the default is in-memory.
var manager = provider.GetRequiredService<IGarminConnectionManager>();
try
{
// "userKey" is your app's own id for the user — the same key their OAuth1 token is stored under.
var created = await manager.CreateWorkoutAsync(userKey, workout);
await manager.ScheduleWorkoutAsync(userKey, created.WorkoutId!.Value, new DateOnly(2026, 8, 20));
}
catch (GarminReconnectRequiredException)
{
// The user has never connected, or their stored token has expired/been revoked —
// prompt them to log in again. The dead token is purged from the store automatically.
}
Connecting a user (once) is a GarminRunerz.Connect.Auth concern: log in, then persist the returned
GarminOAuth1Token under the user key via your IOAuth1TokenStore. After that the manager never
needs a password again.
API
| Method | Description |
|---|---|
CreateWorkoutAsync(workout) |
Creates the workout; returns it with the Garmin-assigned WorkoutId. |
UpdateWorkoutAsync(workout) |
Updates an existing workout (WorkoutId required). |
ScheduleWorkoutAsync(id, date) |
Schedules the workout on the Garmin Connect calendar. |
DeleteWorkoutAsync(id) |
Deletes the workout. |
The registration also exposes the full IGarminConnectClient (activities, devices, SendWorkoutToDevices, …) for anything beyond workout upload.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- GarminRunerz.Connect.Auth (>= 0.5.0)
- GarminRunerz.Workout.Models (>= 2.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.11)
- Unofficial.Garmin.Connect (>= 0.9.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.