suunto-api-wrapper 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,16 @@
1
1
  # suunto-api-wrapper
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/suunto-api-wrapper.svg)](https://www.npmjs.com/package/suunto-api-wrapper)
4
+ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Marius-Ar_suunto-api-wrapper&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Marius-Ar_suunto-api-wrapper)
4
5
 
5
- A small, typed TypeScript client for the **Suunto app API** (which is served by
6
- the Sports Tracker backend at `api.sports-tracker.com`).
6
+ A small, typed TypeScript client for the **Suunto mobile app API** the same
7
+ backend the Suunto phone app talks to (served by Sports Tracker at
8
+ `api.sports-tracker.com`).
9
+
10
+ **No Suunto apizone / developer account required.** This library does **not**
11
+ use Suunto's official partner API at `apizone.suunto.com`, so there are no
12
+ OAuth client IDs, no app registration, and no developer approval to deal with.
13
+ You authenticate with the same email + password you use in the Suunto app.
7
14
 
8
15
  It handles the annoying parts and exposes the endpoints as
9
16
  typed, resource‑grouped methods:
@@ -83,15 +90,25 @@ payload, typed to the real API response shape (an envelope of
83
90
  | `suunto.workouts` | `.own(params?)` | your own workouts |
84
91
  | | `.public(username, params?)` | a user's public workouts |
85
92
  | | `.byKey(username, key, params?)` | a single workout (public, or your own when authed) |
93
+ | | `.stats(username)` | aggregated workout stats per activity |
94
+ | | `.within(box)` | public workouts inside a lat/lng bounding box |
86
95
  | `suunto.users` | `.byName(username)` | a user's public profile |
87
96
  | | `.search(terms)` | search for users |
88
97
  | `suunto.gear` | `.latest(username, params?)` | a user's latest gear |
98
+ | `suunto.wellness` | `.sleep(params?)` | sleep summaries (247) |
99
+ | | `.sleepStages(params?)` | per‑stage sleep intervals (247) |
100
+ | | `.recovery(params?)` | recovery balance + stress state (247) |
101
+ | | `.activity(params?)` | daily activity samples (247) |
89
102
 
90
103
  ```ts
91
104
  // Workouts
92
105
  const own = await suunto.workouts.own({ limit: 20, offset: 0, since: 0 });
93
106
  const publicItems = await suunto.workouts.public("someuser", { limit: 40 });
94
107
  const single = await suunto.workouts.byKey("someuser", "workoutKey123");
108
+ const stats = await suunto.workouts.stats("someuser");
109
+ const nearby = await suunto.workouts.within({
110
+ lowerLat: 45.70, lowerLng: 4.75, upperLat: 45.85, upperLng: 4.95, limit: 50,
111
+ });
95
112
 
96
113
  // Users
97
114
  const profile = await suunto.users.byName("someuser");
@@ -99,8 +116,23 @@ const matches = await suunto.users.search("john");
99
116
 
100
117
  // Gear
101
118
  const gear = await suunto.gear.latest("someuser", { allTypes: true });
119
+
120
+ // 247 wellness data — sleep, recovery, activity
121
+ const sleep = await suunto.wellness.sleep({ since: 0 }); // since = epoch ms; 0 returns all
122
+ const stages = await suunto.wellness.sleepStages({ since: 0 });
123
+ const recov = await suunto.wellness.recovery(); // omit `since` to fetch the default window
124
+ const daily = await suunto.wellness.activity();
102
125
  ```
103
126
 
127
+ #### What is 247 service?
128
+
129
+ The Suunto app exposes a second service at `247.sports-tracker.com` (separate
130
+ from `api.sports-tracker.com`) for **always‑on, around‑the‑clock body data**
131
+ collected by the watch outside recorded workouts.
132
+
133
+ The 247 client lives at `suunto.http247` if you need a raw escape hatch (same
134
+ auth headers). Override the host with the `baseUrl247` client option.
135
+
104
136
  The response payloads are fully typed. For example, workout `extensions` are a
105
137
  discriminated union you can narrow on:
106
138
 
@@ -122,6 +154,7 @@ Some endpoints (like fetching a public profile) don't require login. Use the
122
154
  ```ts
123
155
  const guest = SuuntoClient.unauthenticated();
124
156
  const profile = await guest.users.byName("someuser");
157
+ const stats = await guest.workouts.stats("someuser");
125
158
  ```
126
159
 
127
160
  ### Escape hatch: the raw HTTP client
package/dist/index.d.mts CHANGED
@@ -86,6 +86,68 @@ declare const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15
86
86
  declare function login(options: LoginOptions): Promise<LoginResponse>;
87
87
  declare function sessionTokenFrom(response: LoginResponse): string | undefined;
88
88
 
89
+ interface UserProfile {
90
+ username: string;
91
+ createdDate: number;
92
+ lastModified: number;
93
+ lastLogin: number;
94
+ realName: string;
95
+ /** ISO 3166-1 alpha-2 country code. */
96
+ country: string;
97
+ gender: string;
98
+ uuid: string;
99
+ blocked: boolean;
100
+ showLocale: boolean;
101
+ followersCount: number;
102
+ followingCount: number;
103
+ currentBlobStorageLocation: string;
104
+ defaultBinaryStorageLocation: string;
105
+ }
106
+ interface UserProfileResponse {
107
+ error: string | null;
108
+ payload: UserProfile;
109
+ metadata: {
110
+ ts: string;
111
+ };
112
+ }
113
+ /** User shape returned by the search endpoint (differs slightly from {@link UserProfile}). */
114
+ interface SearchUser {
115
+ username: string;
116
+ createdDate: number;
117
+ lastModified: number;
118
+ lastLogin: number;
119
+ /** Absent on some legacy accounts. */
120
+ realName?: string;
121
+ /** Free-form profile bio. */
122
+ description?: string;
123
+ /** ISO 3166-1 country code. Mostly alpha-2, but legacy records may use alpha-3 (e.g. "FRA"). */
124
+ country?: string;
125
+ city?: string;
126
+ gender: string;
127
+ uuid: string;
128
+ imageKey?: string;
129
+ profileImageUrl?: string;
130
+ coverImageKey?: string;
131
+ coverImageUrl?: string;
132
+ showLocale: boolean;
133
+ defaultBinaryStorageLocation: string;
134
+ currentBlobStorageLocation: string;
135
+ key: string;
136
+ }
137
+ interface UserSearchResult {
138
+ /** Relationship to the searching user, e.g. "STRANGER". */
139
+ connection: string;
140
+ user: SearchUser;
141
+ workout: unknown | null;
142
+ }
143
+ interface UserSearchResponse {
144
+ error: string | null;
145
+ payload: UserSearchResult[];
146
+ metadata: {
147
+ ts: string;
148
+ };
149
+ }
150
+
89
151
  interface GetWorkoutsParams {
90
152
  limit?: number;
91
153
  sortonst?: boolean;
@@ -95,6 +157,142 @@ interface GetOwnWorkoutsParams {
95
157
  offset?: number;
96
158
  limit?: number;
97
159
  }
160
+ /** Bounding-box query for {@link getWorkoutsWithin}. All coords in decimal degrees. */
161
+ interface GetWorkoutsWithinParams {
162
+ /** South latitude of the bounding box. */
163
+ lowerLat: number;
164
+ /** West longitude of the bounding box. */
165
+ lowerLng: number;
166
+ /** North latitude of the bounding box. */
167
+ upperLat: number;
168
+ /** East longitude of the bounding box. */
169
+ upperLng: number;
170
+ /** Max results. Defaults to 50. */
171
+ limit?: number;
172
+ }
173
+ /** Suunto activity type IDs returned by the API as `activityId` / `_id`. */
174
+ declare enum SuuntoActivityType {
175
+ WALKING = 0,
176
+ RUNNING = 1,
177
+ CYCLING = 2,
178
+ CROSS_COUNTRY_SKIING = 3,
179
+ OTHER_1 = 4,
180
+ OTHER_2 = 5,
181
+ OTHER_3 = 6,
182
+ OTHER_4 = 7,
183
+ OTHER_5 = 8,
184
+ OTHER_6 = 9,
185
+ MOUNTAIN_BIKING = 10,
186
+ HIKING = 11,
187
+ ROLLER_SKATING = 12,
188
+ DOWNHILL_SKIING = 13,
189
+ PADDLING = 14,
190
+ ROWING = 15,
191
+ GOLF = 16,
192
+ INDOOR = 17,
193
+ PARKOUR = 18,
194
+ BALLGAMES = 19,
195
+ OUTDOOR_GYM = 20,
196
+ SWIMMING = 21,
197
+ TRAIL_RUNNING = 22,
198
+ GYM = 23,
199
+ NORDIC_WALKING = 24,
200
+ HORSEBACK_RIDING = 25,
201
+ MOTOR_SPORTS = 26,
202
+ SKATEBOARDING = 27,
203
+ WATER_SPORTS = 28,
204
+ CLIMBING = 29,
205
+ SNOWBOARDING = 30,
206
+ SKI_TOURING = 31,
207
+ FITNESS_CLASS = 32,
208
+ SOCCER = 33,
209
+ TENNIS = 34,
210
+ BASKETBALL = 35,
211
+ BADMINTON = 36,
212
+ BASEBALL = 37,
213
+ VOLLEYBALL = 38,
214
+ AMERICAN_FOOTBALL = 39,
215
+ TABLE_TENNIS = 40,
216
+ RACQUETBALL = 41,
217
+ SQUASH = 42,
218
+ FLOORBALL = 43,
219
+ HANDBALL = 44,
220
+ SOFTBALL = 45,
221
+ BOWLING = 46,
222
+ CRICKET = 47,
223
+ RUGBY = 48,
224
+ ICE_SKATING = 49,
225
+ ICE_HOCKEY = 50,
226
+ YOGA = 51,
227
+ INDOOR_CYCLING = 52,
228
+ TREADMILL = 53,
229
+ CROSSFIT = 54,
230
+ CROSSTRAINER = 55,
231
+ ROLLER_SKIING = 56,
232
+ INDOOR_ROWING = 57,
233
+ STRETCHING = 58,
234
+ TRACK_AND_FIELD = 59,
235
+ ORIENTEERING = 60,
236
+ SUP = 61,
237
+ COMBAT_SPORTS = 62,
238
+ KETTLEBELL = 63,
239
+ DANCING = 64,
240
+ SNOWSHOEING = 65,
241
+ FRISBEE_GOLF = 66,
242
+ FUTSAL = 67,
243
+ MULTISPORT = 68,
244
+ AEROBICS = 69,
245
+ TREKKING = 70,
246
+ SAILING = 71,
247
+ KAYAKING = 72,
248
+ CIRCUIT_TRAINING = 73,
249
+ TRIATHLON = 74,
250
+ PADEL = 75,
251
+ CHEERLEADING = 76,
252
+ BOXING = 77,
253
+ SCUBADIVING = 78,
254
+ FREEDIVING = 79,
255
+ ADVENTURE_RACING = 80,
256
+ GYMNASTICS = 81,
257
+ CANOEING = 82,
258
+ MOUNTAINEERING = 83,
259
+ TELEMARKSKIING = 84,
260
+ OPENWATER_SWIMMING = 85,
261
+ WINDSURFING = 86,
262
+ KITESURFING_KITING = 87,
263
+ PARAGLIDING = 88,
264
+ SNORKELING = 90,
265
+ SURFING = 91,
266
+ SWIMRUN = 92,
267
+ DUATHLON = 93,
268
+ AQUATHLON = 94,
269
+ OBSTACLE_RACING = 95,
270
+ FISHING = 96,
271
+ HUNTING = 97,
272
+ GRAVEL_CYCLING = 99,
273
+ MERMAIDING = 100,
274
+ SPEARFISHING = 101,
275
+ JUMP_ROPE = 102,
276
+ TRACK_RUNNING = 103,
277
+ CALISTHENICS = 104,
278
+ E_BIKING = 105,
279
+ E_MTB = 106,
280
+ BACKCOUNTRY_SKIING = 107,
281
+ WHEELCHAIR = 108,
282
+ HAND_CYCLING = 109,
283
+ SPLIT_BOARDING = 110,
284
+ BIATHLON = 111,
285
+ MEDITATION = 112,
286
+ FIELD_HOCKEY = 113,
287
+ CYCLOCROSS = 114,
288
+ VERTICAL_RUN = 115,
289
+ SKI_MOUNTAINEERING = 116,
290
+ SKATE_SKIING = 117,
291
+ CLASSIC_SKIING = 118,
292
+ CHORES = 119,
293
+ PILATES = 120,
294
+ NEW_YOGA = 121
295
+ }
98
296
  /** Valid values for the `extensions` query param on the single-workout endpoint. */
99
297
  declare enum WorkoutExtensionName {
100
298
  Dive = "DiveExtension",
@@ -312,8 +510,7 @@ type WorkoutExtension = FitnessExtension | IntensityExtension | SummaryExtension
312
510
  interface Workout {
313
511
  username: string;
314
512
  sharingFlags: number;
315
- /** Suunto activity type ID (e.g. 2 = cycling, 11 = trail running, 21 = pool swim, 36 = gym, 99 = other). */
316
- activityId: number;
513
+ activityId: SuuntoActivityType;
317
514
  key: string;
318
515
  startTime: number;
319
516
  stopTime: number;
@@ -341,7 +538,8 @@ interface Workout {
341
538
  tss: TssEntry;
342
539
  tssList: TssEntry[];
343
540
  suuntoTags: string[];
344
- clientCalculatedAchievements: ClientCalculatedAchievements;
541
+ /** Absent on some workouts (e.g. when the user has no achievements yet). */
542
+ clientCalculatedAchievements?: ClientCalculatedAchievements;
345
543
  workoutKey: string;
346
544
  visibilityFacebook: boolean;
347
545
  visibilityTwitter: boolean;
@@ -370,6 +568,16 @@ interface Workout {
370
568
  comments?: WorkoutComment[];
371
569
  /** Only present when reactionCount > 0. */
372
570
  reactions?: WorkoutReaction[];
571
+ /** Owner's display name. Returned on feed-style endpoints (e.g. `within`). */
572
+ fullname?: string;
573
+ /** Owner's profile picture URL. Returned on feed-style endpoints. */
574
+ userPhoto?: string;
575
+ /** Owner's cover photo URL. Returned on feed-style endpoints. */
576
+ coverPhoto?: string;
577
+ /** Free-text achievement labels (e.g. "Fastest time on this route"). */
578
+ achievements?: string[];
579
+ /** Average power, watts. Mirrors `SummaryExtension.avgPower` on feed responses. */
580
+ avgPower?: number;
373
581
  }
374
582
  interface WorkoutsResponse {
375
583
  error: string | null;
@@ -384,9 +592,72 @@ interface WorkoutResponse {
384
592
  payload: Workout;
385
593
  metadata: Record<string, unknown>;
386
594
  }
595
+ /** Single item from the {@link getWorkoutsWithin} feed: owner + workout. */
596
+ interface WorkoutsWithinItem {
597
+ user: SearchUser;
598
+ workout: Workout;
599
+ }
600
+ interface WorkoutsWithinResponse {
601
+ error: string | null;
602
+ payload: WorkoutsWithinItem[];
603
+ metadata: {
604
+ workoutcount: string;
605
+ };
606
+ }
607
+ /** Aggregated totals for a single activity type. */
608
+ interface WorkoutStatsEntry {
609
+ /** Suunto activity type ID this entry aggregates. */
610
+ _id: SuuntoActivityType;
611
+ /** Metres. */
612
+ totalDistance: number;
613
+ /** Seconds. */
614
+ totalTime: number;
615
+ /** Kilocalories. */
616
+ energyConsumption: number;
617
+ /** Number of workouts of this activity. */
618
+ numberOfWorkouts: number;
619
+ /** Metres. Only meaningful for dives. */
620
+ maxDepth: number;
621
+ }
622
+ interface WorkoutStats {
623
+ /** Sum of distances across all activities, in metres. */
624
+ totalDistanceSum: number;
625
+ /** Sum of times across all activities, in seconds. */
626
+ totalTimeSum: number;
627
+ /** Sum of energy consumption across all activities, in kilocalories. */
628
+ totalEnergyConsumptionSum: number;
629
+ /** Total workout count across all activities. */
630
+ totalNumberOfWorkoutsSum: number;
631
+ /** Number of distinct days that have at least one workout. */
632
+ totalDays: number;
633
+ /**
634
+ * Per-activity aggregates, restricted to a curated set of activity types
635
+ * (the ones the official UI surfaces as headline cards).
636
+ */
637
+ allStats: WorkoutStatsEntry[];
638
+ /** Per-activity aggregates covering every activity type the user has recorded. */
639
+ allActualStats: WorkoutStatsEntry[];
640
+ }
641
+ interface WorkoutStatsResponse {
642
+ error: string | null;
643
+ payload: WorkoutStats;
644
+ metadata: {
645
+ ts: string;
646
+ };
647
+ }
387
648
 
388
649
  declare function getWorkouts(client: HttpClient, username: string, params?: GetWorkoutsParams): Promise<WorkoutsResponse>;
389
650
  declare function getOwnWorkouts(client: HttpClient, params?: GetOwnWorkoutsParams): Promise<WorkoutsResponse>;
651
+ /**
652
+ * Public workouts whose center position falls inside the given geographic
653
+ * bounding box. Used by the "explore nearby" map view. Unauthenticated.
654
+ */
655
+ declare function getWorkoutsWithin(client: HttpClient, params: GetWorkoutsWithinParams): Promise<WorkoutsWithinResponse>;
656
+ /**
657
+ * Aggregated workout stats per activity for a user. Unauthenticated — no
658
+ * session required.
659
+ */
660
+ declare function getWorkoutStats(client: HttpClient, username: string): Promise<WorkoutStatsResponse>;
390
661
  /** Workout endpoints, bound to an {@link HttpClient}. Accessed via `suunto.workouts`. */
391
662
  declare class WorkoutsResource {
392
663
  private readonly client;
@@ -400,63 +671,13 @@ declare class WorkoutsResource {
400
671
  * and, when the client is authenticated as the owner, for private ones too.
401
672
  */
402
673
  byKey(username: string, workoutKey: string, params?: GetWorkoutParams): Promise<WorkoutResponse>;
403
- }
404
-
405
- interface UserProfile {
406
- username: string;
407
- createdDate: number;
408
- lastModified: number;
409
- lastLogin: number;
410
- realName: string;
411
- /** ISO 3166-1 alpha-2 country code. */
412
- country: string;
413
- gender: string;
414
- uuid: string;
415
- blocked: boolean;
416
- showLocale: boolean;
417
- followersCount: number;
418
- followingCount: number;
419
- currentBlobStorageLocation: string;
420
- defaultBinaryStorageLocation: string;
421
- }
422
- interface UserProfileResponse {
423
- error: string | null;
424
- payload: UserProfile;
425
- metadata: {
426
- ts: string;
427
- };
428
- }
429
- /** User shape returned by the search endpoint (differs slightly from {@link UserProfile}). */
430
- interface SearchUser {
431
- username: string;
432
- createdDate: number;
433
- lastModified: number;
434
- lastLogin: number;
435
- realName: string;
436
- /** ISO 3166-1 country code. Mostly alpha-2, but legacy records may use alpha-3 (e.g. "FRA"). */
437
- country?: string;
438
- city?: string;
439
- gender: string;
440
- uuid: string;
441
- imageKey?: string;
442
- profileImageUrl?: string;
443
- showLocale: boolean;
444
- defaultBinaryStorageLocation: string;
445
- currentBlobStorageLocation: string;
446
- key: string;
447
- }
448
- interface UserSearchResult {
449
- /** Relationship to the searching user, e.g. "STRANGER". */
450
- connection: string;
451
- user: SearchUser;
452
- workout: unknown | null;
453
- }
454
- interface UserSearchResponse {
455
- error: string | null;
456
- payload: UserSearchResult[];
457
- metadata: {
458
- ts: string;
459
- };
674
+ /**
675
+ * Aggregated workout stats per activity for the given user. Works
676
+ * unauthenticated.
677
+ */
678
+ stats(username: string): Promise<WorkoutStatsResponse>;
679
+ /** Public workouts whose center falls inside a geographic bounding box. */
680
+ within(params: GetWorkoutsWithinParams): Promise<WorkoutsWithinResponse>;
460
681
  }
461
682
 
462
683
  /**
@@ -503,6 +724,94 @@ declare class GearResource {
503
724
  latest(username: string, params?: GetLatestGearParams): Promise<GearResponse>;
504
725
  }
505
726
 
727
+ /** Default export host for the 247 service. */
728
+ declare const SPORTS_TRACKER_247_API = "https://247.sports-tracker.com";
729
+ interface ExportParams {
730
+ /** Epoch milliseconds. Only entries with a timestamp `>= since` are returned. Use 0 to retrieve all data */
731
+ since?: number;
732
+ }
733
+ /**
734
+ * One row from a 247 NDJSON export stream. Every row wraps a typed payload
735
+ * under `entryData` plus a top-level ISO `timestamp` (with offset).
736
+ */
737
+ interface ExportEntry<T> {
738
+ timestamp: string;
739
+ entryData: T;
740
+ }
741
+ /** Sleep summary payload (durations are in seconds). */
742
+ interface SleepSummaryData {
743
+ sleepId: number;
744
+ bedtimeStart: string;
745
+ bedtimeEnd: string;
746
+ duration: number;
747
+ deepSleepDuration: number;
748
+ lightSleepDuration: number;
749
+ remSleepDuration: number;
750
+ sleepOnsetLatencyDuration: number;
751
+ wakeAfterSleepOnsetDuration: number;
752
+ wakeBeforeOffBedDuration: number;
753
+ hrAvg: number;
754
+ hrMin: number;
755
+ isNap: boolean;
756
+ quality?: number;
757
+ maxSpo2?: number;
758
+ altitude?: number;
759
+ avgHrv?: number;
760
+ avgHrvSampleCount?: number;
761
+ }
762
+ type SleepStage = "AWAKE" | "REM" | "LIGHT" | "DEEP";
763
+ /** Single sleep-stage interval. `duration` is in seconds. */
764
+ interface SleepStageData {
765
+ stage: SleepStage;
766
+ duration: number;
767
+ }
768
+ /** Recovery sample. `balance` is in [0, 1]; `stressState` is an integer code. */
769
+ interface RecoveryData {
770
+ balance: number;
771
+ stressState: number;
772
+ }
773
+ /** Activity sample. `energyConsumption` is in joules. */
774
+ interface ActivityData {
775
+ hr: number;
776
+ stepCount: number;
777
+ energyConsumption: number;
778
+ }
779
+ type SleepSummary = ExportEntry<SleepSummaryData>;
780
+ type SleepStageInterval = ExportEntry<SleepStageData>;
781
+ type RecoveryEntry = ExportEntry<RecoveryData>;
782
+ type ActivityEntry = ExportEntry<ActivityData>;
783
+ /**
784
+ * 247 `/export` endpoints stream `application/x-ndjson`. The HTTP layer parses
785
+ * each line into one record, so each function resolves with an array of
786
+ * records.
787
+ */
788
+ type SleepExportResponse = SleepSummary[];
789
+ type SleepStagesExportResponse = SleepStageInterval[];
790
+ type RecoveryExportResponse = RecoveryEntry[];
791
+ type ActivityExportResponse = ActivityEntry[];
792
+
793
+ /** Sleep summaries from the 247 service. */
794
+ declare function getSleepExport(client: HttpClient, params?: ExportParams): Promise<SleepExportResponse>;
795
+ /** Per-stage sleep intervals from the 247 service. */
796
+ declare function getSleepStagesExport(client: HttpClient, params?: ExportParams): Promise<SleepStagesExportResponse>;
797
+ /** Recovery entries (balance + stress state) from the 247 service. */
798
+ declare function getRecoveryExport(client: HttpClient, params?: ExportParams): Promise<RecoveryExportResponse>;
799
+ /** Daily activity entries from the 247 service. */
800
+ declare function getActivityExport(client: HttpClient, params?: ExportParams): Promise<ActivityExportResponse>;
801
+ /**
802
+ * Endpoints served by `https://247.sports-tracker.com` (sleep, recovery,
803
+ * activity). Bound to a dedicated {@link HttpClient} configured against that
804
+ * host. Accessed via `suunto.wellness`.
805
+ */
806
+ declare class WellnessResource {
807
+ private readonly client;
808
+ constructor(client: HttpClient);
809
+ sleep(params?: ExportParams): Promise<SleepExportResponse>;
810
+ sleepStages(params?: ExportParams): Promise<SleepStagesExportResponse>;
811
+ recovery(params?: ExportParams): Promise<RecoveryExportResponse>;
812
+ activity(params?: ExportParams): Promise<ActivityExportResponse>;
813
+ }
814
+
506
815
  interface SuuntoClientOptions extends Omit<HttpClientOptions, "beforeRequest"> {
507
816
  /**
508
817
  * Account email. Drives the `x-totp` header. Omit it (e.g. via
@@ -513,6 +822,8 @@ interface SuuntoClientOptions extends Omit<HttpClientOptions, "beforeRequest"> {
513
822
  /** Session key from login — sent as the `Sttauthorization` header. */
514
823
  sessionKey?: string;
515
824
  userAgent?: string;
825
+ /** Override host for the 247 service (sleep/recovery/activity). */
826
+ baseUrl247?: string;
516
827
  }
517
828
  /**
518
829
  * The main entry point. Owns the authenticated {@link HttpClient} and exposes
@@ -529,9 +840,12 @@ declare class SuuntoClient {
529
840
  readonly http: HttpClient;
530
841
  /** Session key in use, if the client was authenticated. */
531
842
  readonly sessionKey?: string;
843
+ /** Dedicated HTTP client for the 247 host (sleep/recovery/activity). */
844
+ readonly http247: HttpClient;
532
845
  readonly workouts: WorkoutsResource;
533
846
  readonly users: UsersResource;
534
847
  readonly gear: GearResource;
848
+ readonly wellness: WellnessResource;
535
849
  constructor(options: SuuntoClientOptions);
536
850
  /** Log in with email/password and return an authenticated client. */
537
851
  static login(options: LoginOptions & Omit<SuuntoClientOptions, "email" | "sessionKey">): Promise<SuuntoClient>;
@@ -542,4 +856,4 @@ declare class SuuntoClient {
542
856
  static unauthenticated(options?: Omit<SuuntoClientOptions, "email" | "sessionKey">): SuuntoClient;
543
857
  }
544
858
 
545
- export { type ActivityCounts, type Cadence, type ClientCalculatedAchievements, type CumulativeAchievement, DEFAULT_USER_AGENT, type FitnessExtension, type Gear, GearResource, type GearResponse, type GearSummary, type GetLatestGearParams, type GetOwnWorkoutsParams, type GetWorkoutsParams, type HeartRateRecovery, type HrData, HttpClient, type HttpClientOptions, HttpError, type HttpResponse, type IntensityExtension, type IntensityZone, type IntensityZones, type LoginOptions, type LoginResponse, type PersonalBestAchievement, type Position, type Query, type Rankings, type RequestBody, type RequestContext, type RequestOptions, type RouteRanking, SPORTS_TRACKER_API, type SearchUser, type SummaryExtension, SuuntoClient, type SuuntoClientOptions, type SwimmingHeaderExtension, type TssEntry, type UserProfile, type UserProfileResponse, type UserSearchResponse, type UserSearchResult, UsersResource, type WeatherExtension, type Workout, type WorkoutComment, type WorkoutExtension, type WorkoutReaction, WorkoutsResource, type WorkoutsResponse, generateXtotp, getLatestGear, getOwnWorkouts, getUserByName, getWorkouts, login, searchUsers, secondsUntilRollover, sessionTokenFrom };
859
+ export { type ActivityCounts, type ActivityData, type ActivityEntry, type ActivityExportResponse, type Cadence, type ClientCalculatedAchievements, type CumulativeAchievement, DEFAULT_USER_AGENT, type ExportEntry, type ExportParams, type FitnessExtension, type Gear, GearResource, type GearResponse, type GearSummary, type GetLatestGearParams, type GetOwnWorkoutsParams, type GetWorkoutsParams, type GetWorkoutsWithinParams, type HeartRateRecovery, type HrData, HttpClient, type HttpClientOptions, HttpError, type HttpResponse, type IntensityExtension, type IntensityZone, type IntensityZones, type LoginOptions, type LoginResponse, type PersonalBestAchievement, type Position, type Query, type Rankings, type RecoveryData, type RecoveryEntry, type RecoveryExportResponse, type RequestBody, type RequestContext, type RequestOptions, type RouteRanking, SPORTS_TRACKER_247_API, SPORTS_TRACKER_API, type SearchUser, type SleepExportResponse, type SleepStage, type SleepStageData, type SleepStageInterval, type SleepStagesExportResponse, type SleepSummary, type SleepSummaryData, type SummaryExtension, SuuntoActivityType, SuuntoClient, type SuuntoClientOptions, type SwimmingHeaderExtension, type TssEntry, type UserProfile, type UserProfileResponse, type UserSearchResponse, type UserSearchResult, UsersResource, type WeatherExtension, WellnessResource, type Workout, type WorkoutComment, type WorkoutExtension, type WorkoutReaction, type WorkoutStats, type WorkoutStatsEntry, type WorkoutStatsResponse, WorkoutsResource, type WorkoutsResponse, type WorkoutsWithinItem, type WorkoutsWithinResponse, generateXtotp, getActivityExport, getLatestGear, getOwnWorkouts, getRecoveryExport, getSleepExport, getSleepStagesExport, getUserByName, getWorkoutStats, getWorkouts, getWorkoutsWithin, login, searchUsers, secondsUntilRollover, sessionTokenFrom };