Custom calculations¶
When your app needs activity data, fetch it from SweatStack. Don't mirror activities into your own database. SweatStack already stores them, and any mirror you build adds a sync problem you don't need.
The one thing you do need to store somewhere is the output of your own calculations on activities: a training-stress score, custom zones, a classifier that labels the type of session. This guide covers how to work through a user's activity history reliably, and where to put what you compute.
Track what you've processed by ID, not by timestamp¶
To know which activities you have already processed, keep a set of activity IDs. Don't track them by start timestamp ("I've handled everything before T, so only process newer ones").
The reason: the backfill ingests a user's history from newest to oldest on a best-effort basis, so an older activity can finish ingesting after a newer one. A timestamp filter misses that late arrival. ID tracking avoids the ordering problem entirely.
Where to store the derived value¶
Two options: app metadata, or your own database.
App metadata keeps the value attached to the SweatStack entity it belongs to, so you need no database of your own. There are two endpoints, depending on whether the value is per activity or per user.
For per-activity values (training stress, custom zones, classifier output), PUT it on the activity:
PUT /api/v1/activities/{id}/app-metadata
{"training_stress": 87, "version": 2}
In Python, with the client of a user signed in through your app: user.client.activities.app_metadata.set(activity_id, data={"training_stress": 87, "version": 2}).
The version field doubles as your "processed" flag: if app_metadata.version matches your current code, the activity is done. You need no separate set of activity IDs, because each activity carries its own status. Bump the version when the calculation changes, and existing activities are processed again on the next read. The limit is 1024 bytes of JSON.
For per-user values (28-day CTL, an FTP estimate, a readiness score, anything longitudinal), PUT it on the user:
PUT /api/v1/profile/app-metadata
{"ctl_28d": 62.4, "computed_through_activity": "act_01HV..."}
In Python: user.client.profile.app_metadata.set(data={...}). The limit is 4096 bytes of JSON.
Your own database is the fallback when you outgrow those limits: large model outputs, long text, deeply nested structures. Keep the same shape: keyed by activity_id or user_id, holding your derived values only, never a mirror of activity data.
Next steps¶
- App metadata: full request and response shapes, scopes, and size limits per entity type.
- Activities: the activity payload, and the
summary.*aggregates SweatStack already computes. Check those before you compute your own. - Onboarding and backfill: what to render during the first session while history is still arriving.
- The
activity_createdwebhook: process new activities as they arrive instead of checking again on every load.