Skip to content

Application metadata

Your app can attach its own JSON to activities, traces, tests, and users. The metadata is keyed by the app that wrote it, scoped to a single user, and visible only to that app.

Use it when your app needs to store a small amount of state next to SweatStack data without running its own database. Examples:

  • A coaching app annotating activities with workout-plan IDs.
  • A recovery app marking traces with its own readiness scores.
  • An analytics app storing per-user feature flags without a profile store of its own.

Other apps reading the same entities never see your metadata. SweatStack does not interpret it.

Requirements

The app metadata endpoints require an application token: an access token issued through OAuth2 to your registered app. A personal token, such as an API key from Settings → API, cannot read or write metadata.

If your token has no aud (audience) claim that identifies an application, every metadata endpoint returns 403.

Requires the data:write scope.

Endpoints

The same shape applies to four entity types.

Entity Upsert Delete
Activity PUT /api/v1/activities/{id}/app-metadata DELETE /api/v1/activities/{id}/app-metadata
Trace PUT /api/v1/traces/{id}/app-metadata DELETE /api/v1/traces/{id}/app-metadata
Test PUT /api/v1/tests/{id}/app-metadata DELETE /api/v1/tests/{id}/app-metadata
User (the authenticated user) PUT /api/v1/profile/app-metadata DELETE /api/v1/profile/app-metadata

Write metadata

The PUT body is a free-form JSON object. SweatStack constrains only its size and nesting depth (see Limits).

user.client.activities.app_metadata.set(
    activity_id,
    data={
        "workout_plan_id": "plan_42",
        "intended_intensity": "z2",
        "notes": "easy recovery ride",
    },
)

Use the client of a user who signed in through your app: user.client in the FastAPI helper, auth.client in the Streamlit helper. A client from sweatstack.authenticate() or a personal API key holds a personal token, and the API returns 403. The methods are activities.app_metadata.set, traces.app_metadata.set, tests.app_metadata.set and profile.app_metadata.set.

curl -X PUT "https://app.sweatstack.no/api/v1/activities/{activity_id}/app-metadata" \
    -H "Authorization: Bearer {your_app_access_token}" \
    -H "Content-Type: application/json" \
    -d '{
        "workout_plan_id": "plan_42",
        "intended_intensity": "z2",
        "notes": "easy recovery ride"
    }'

PUT replaces the whole metadata object. There is no merge or patch operation. If you need to change one field, read the existing metadata first and send the whole object back.

Delete metadata

user.client.activities.app_metadata.delete(activity_id)
curl -X DELETE "https://app.sweatstack.no/api/v1/activities/{activity_id}/app-metadata" \
    -H "Authorization: Bearer {your_app_access_token}"

The API returns 204 on success, also when no metadata exists.

Read metadata back

When your app reads an entity, SweatStack merges your metadata into the response under an app_metadata field. When a different app reads the same entity, app_metadata holds that app's own metadata or is null. It never holds both.

activity = user.client.activities.retrieve(activity_id)
activity.app_metadata
curl -X GET "https://app.sweatstack.no/api/v1/activities/{activity_id}" \
    -H "Authorization: Bearer {your_app_access_token}"

The response includes:

{
    "id": "...",
    "sport": "cycling.road",
    "start": "2026-05-06T08:00:00Z",
    "...": "...",
    "app_metadata": {
        "workout_plan_id": "plan_42",
        "intended_intensity": "z2",
        "notes": "easy recovery ride"
    }
}

Limits

Entity Maximum size (raw JSON body) Maximum nesting depth
Activity 1024 bytes 32
Trace 1024 bytes 32
Test 1024 bytes 32
User 4096 bytes 32

If the body exceeds the size limit, the API returns 413. If it exceeds the depth limit, the API returns 422.

Patterns

  • Use it for app state, not for user data. Metadata is keyed to your app and invisible to others. If the user revokes your app's authorization, your metadata is no longer reachable.
  • Don't store personal data the user wouldn't expect. Metadata follows the user's data and is included in their export. For metadata you write, SweatStack acts as your processor under our DPA. You remain the controller of what you store.
  • Don't use it as a primary store. Per-entity round trips are not a substitute for an indexed database. For app state you need to query, run a small store on your side.
  • Do use it for small, per-entity annotations. Workout plan IDs, intent labels, your own scoring outputs. Things that belong with the SweatStack record.