Data Mirror Setup Guide
This guide explains how to create an automated JSON data mirror of your Intervals.icu training data for use with AI coaching systems.
Prefer an interactive guide? Paste SETUP_ASSISTANT.md into any AI chat and it will walk you through this entire setup step by step.
Running an agentic platform locally? If your AI coach runs on the same machine as your data (OpenClaw, Claude Code, Cowork, etc.), consider local sync instead (simpler setup, and no GitHub repository in the path). Where your data goes still depends on what you configure and which AI you point at it; see Privacy & Security.
Overview
The data mirror syncs your Intervals.icu metrics to a GitHub repository on a schedule, every 30 minutes by default, producing JSON files that AI systems can read through a GitHub connector or other authenticated repository access, or by URL for a public repository. GitHub runs scheduled workflows on a best-effort basis, so a scheduled sync can be late or skipped. For fresh data right after a workout, use Sync Now. See Sync Schedule and GitHub Actions Usage before choosing a different schedule.
Result: these files in your repository, shown here as raw URL patterns. Plain raw URLs work without GitHub authentication only for a public repository; for a private repository, see Step 6 and Usage with AI.
https://raw.githubusercontent.com/[you]/[repo]/main/latest.jsonhttps://raw.githubusercontent.com/[you]/[repo]/main/history.jsonhttps://raw.githubusercontent.com/[you]/[repo]/main/ftp_history.jsonhttps://raw.githubusercontent.com/[you]/[repo]/main/intervals.jsonhttps://raw.githubusercontent.com/[you]/[repo]/main/routes.json(always written; route entries only for planned events with GPX/TCX attachments)https://raw.githubusercontent.com/[you]/[repo]/main/saved_workouts.json
Prerequisites
- Intervals.icu account with training data
- GitHub account
- 10 minutes for setup
Step 1: Get Your Intervals.icu Credentials
Finding your credentials:
- Athlete ID: Intervals.icu → Settings → bottom of page (e.g.,
i123456) - API Key: Intervals.icu → Settings → Developer Settings → API Key
Step 2: Set Up GitHub Repository
Create a new private repo
Don't fork Section 11 for this. Forks of a public repository are always public and their visibility cannot be changed. A forked data repo would publish your training data.
- Go to github.com/new
- Name it something like
training-data - Set to Private. See the connector table for platform support.
- Check Add a README file
- Click Create repository
Then add these files to your repository:
| File | Location | Description |
|---|---|---|
sync.py |
Root | The sync script |
auto-sync.yml |
.github/workflows/ |
GitHub Actions workflow |
DATA_REPO_README_TEMPLATE.md |
Root (rename to README.md) |
README template with sync badge and Sync Now link |
To create the workflow folder:
- Click "Add file" → "Create new file"
- Name it:
.github/workflows/auto-sync.yml - Paste the workflow content from examples/json-auto-sync/auto-sync.yml
- Commit
The workflow syncs every 30 minutes by default. To use Fast mode or a hybrid schedule instead, edit its schedule block as described in Sync Schedule and GitHub Actions Usage.
Step 3: Add Repository Secrets
- Go to your repo → Settings → Secrets and variables → Actions
- Click New repository secret
- Add these secrets:
| Secret Name | Value |
|---|---|
ATHLETE_ID |
Your Intervals.icu athlete ID (e.g., i123456) |
INTERVALS_KEY |
Your Intervals.icu API key |
Optional: If your training week starts on a day other than Monday, add one more secret:
| Secret Name | Value |
|---|---|
WEEK_START |
Training week start day: mon, tue, wed, thu, fri, sat, or sun |
If not set, defaults to mon (ISO week). This controls phase detection windows (ensures deload/build classification aligns with your actual training week structure).
Optional: If you want HR zones used for aggregations in specific sports (e.g., running with auto-generated watch power):
| Secret Name | Value |
|---|---|
ZONE_PREFERENCE |
Per-sport zone override, e.g. run:hr,cycling:power |
Only override what you need. Unspecified sports default to power-preferred with HR fallback. Valid values per sport are power or hr. Sport families: cycling, run, ski, rowing, swim, walk, strength, other.
Note: GITHUB_TOKEN is provided automatically by GitHub Actions.
Step 4: Enable Workflow Permissions
- Still in Settings, click Actions → General in the left sidebar
- Scroll down to "Workflow permissions"
- Select "Read and write permissions"
- Click Save
This allows the sync workflow to commit updated data files to the repo. The workflow file also requests contents: write for itself. Whether that request alone is enough without this repository setting has not yet been validated, so keep this setting for now. If GitHub refuses the push, the run fails with an error in its log; it does not fail silently.
Step 5: Enable GitHub Actions
- Go to your repo → Actions tab
- If prompted, enable workflows
- Click on "Auto-Sync Intervals.icu Data" workflow
- Click Run workflow → Run workflow (green button)
Leave the branch selector on your default branch (normally main). A manual run started from any other branch stops at its first step without syncing.
Wait 30-60 seconds, then check if latest.json has been updated. This manual run is the same Sync Now you can use at any time.
If the run fails with a permission error, go back and check that workflow permissions are set to "Read and write" in Step 4.
Step 6: Verify
Check the result in your repository's normal Code view on github.com:
- Confirm that the latest commit is a new
Sync training datacommit - Open
latest.jsonand check thatmetadata.last_updatedchanged to the time of this sync - Open
history.json,ftp_history.json,intervals.json,routes.jsonandsaved_workouts.jsonto confirm they are there
Do not check a private repository with plain raw.githubusercontent.com URLs. They normally return 404 for a private repository when the request carries no GitHub authentication, and being signed in to github.com in your browser does not make them a reliable check. When your AI needs the files, use a GitHub connector or other authenticated repository access (see Usage with AI).
For reference, each file's raw URL follows this pattern. These URLs are for a public repository, or for tooling that explicitly supplies authenticated GitHub access:
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/latest.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/history.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/ftp_history.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/intervals.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/routes.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/saved_workouts.json
latest.json: current 7-day snapshot with activities, wellness, fitness metrics, and derived Section 11 values. Rewritten on every successful sync.history.json: longitudinal data with tiered granularity: daily (90 days), weekly (180 days), and monthly (up to 3 years). Generated automatically on first run, regenerated when outdated.intervals.json: per-interval segment data for recent structured sessions (14-day retention). Written on every successful sync; its activity list can be empty when no recent activity has detected interval structure.routes.json: route/terrain data for planned events with GPX/TCX attachments. Includes climb/descent detection, course character, and polyline. Written on every successful sync, with"events": []when no planned event has an applicable attachment. Parsed routes are cached by attachment ID.saved_workouts.json: read-only mirror of your Intervals.icu saved workouts, with folders and complete workout definitions. Refreshed on its own 6-hour throttle rather than every sync, and the last good copy is kept when a refresh fails.
Sync Schedule and GitHub Actions Usage
Sync Now
Sync Now is the way to get fresh data right after a workout. Scheduled syncs are best-effort and can arrive up to a full interval later, or not at all.
- Open your data repository and tap 🔄 Sync Now in its README, or go to Actions → Auto-Sync Intervals.icu Data
- Click Run workflow, keep your default branch selected, and confirm
- When the run completes, the new data is committed to the repository
A manual run also uploads the generated JSON files as a training-data artifact on the run page, kept for seven days, for uploading to an AI chat. Sync Now requests a run; it does not guarantee that the run starts at once. All syncs share one queue, so if another sync is already running or waiting, your run waits its turn and can start later. GitHub does not guarantee the order of waiting runs. Each manual run counts toward your GitHub Actions minutes like any other run.
Default schedule
- cron: '7,37 * * * *'
- Checks every 30 minutes, at 7 and 37 minutes past each hour (UTC)
- The offsets avoid the start of the hour, which GitHub documents as its busiest time for scheduled workflows
- When GitHub runs the schedule on time, new data in Intervals.icu waits on average 15 minutes, and at most 30 minutes, for the next scheduled sync
- GitHub can still delay or drop scheduled runs, so Sync Now remains the way to request a sync when you need one
GitHub Actions minutes
In a private repository, every run uses your account's GitHub Actions minutes. GitHub rounds each job up to a whole minute, so a sync that finishes in under a minute still counts as one billed minute. The monthly allowance for standard runners is 2,000 minutes on GitHub Free and 3,000 minutes on GitHub Pro and Team, shared by all private-repository workflows in your account. See GitHub's billing documentation for current figures. What happens after the allowance is used depends on your account's payment and budget settings.
Estimates below count scheduled jobs in a 31-day month with every scheduled run executed, assuming each job uses one billed minute. They are not a true worst case: a job that runs longer than a minute, such as a first sync that builds history.json, uses more, and Sync Now runs, failed runs, re-runs and other workflows come on top.
| Schedule | Jobs per day | Jobs in 31 days | GitHub Free (2,000) | Pro or Team (3,000) |
|---|---|---|---|---|
| Default, every 30 minutes | 48 | 1,488 | 512 minutes left | 1,512 minutes left |
| Fast mode, every 15 minutes | 96 | 2,976 | Exceeds the allowance by 976 | 24 minutes left |
- The minutes left must also cover Sync Now runs, failed runs, re-runs and any other workflows in your account, such as
push-workout.yml - GitHub sometimes delays or skips scheduled runs, so real usage can be lower. Do not rely on that: budget for the full count
- If you do not know your plan, assume GitHub Free
- Public repositories do not use standard-runner minutes, but a public repository publishes all of your committed training data. See Privacy Notes
Fast mode
- cron: '7,22,37,52 * * * *'
Fast mode checks every 15 minutes, all day. It is not suitable for GitHub Free unless you have intentionally set up paid usage beyond the allowance, and it uses almost all of a Pro or Team allowance before any manual runs or other workflows.
Advanced hybrid schedules
A hybrid schedule checks more often during an active window that you set in UTC, and hourly otherwise. It uses two ordinary cron lines, which GitHub reads in UTC like the Default and Fast schedules. Do not add a timezone: line to either entry: Section 11 does not rely on it, because in testing an entry with timezone: ran as if its hours were in UTC. Sync Now remains the way to request fresh data when you need it.
The window is fixed in UTC, and the schedule does not adjust itself. Where daylight saving time applies, the same UTC window normally moves by one hour on your local clock when your UTC offset changes. To keep the same local-clock hours all year, recalculate the UTC hours after each offset change and replace both lines yourself; this is optional manual maintenance. With a half-hour or quarter-hour UTC offset, the window starts and ends partway through a local hour. That is expected: the UTC hours remain authoritative.
Standard hybrid: 16 active hours (06:00 to 21:59 UTC), every 20 minutes while active, hourly otherwise. 56 jobs a day, 1,736 in 31 days, leaving 264 minutes of a GitHub Free allowance.
on:
schedule:
- cron: '7,27,47 6-21 * * *'
- cron: '7 0-5,22-23 * * *'
workflow_dispatch:
Extended hybrid (lower headroom): 18 active hours (06:00 to 23:59 UTC), every 20 minutes while active, hourly otherwise. 60 jobs a day, 1,860 in 31 days, leaving only 140 minutes of a GitHub Free allowance.
on:
schedule:
- cron: '7,27,47 6-23 * * *'
- cron: '7 0-5 * * *'
workflow_dispatch:
Fast hybrid (lower headroom): at most 12 active hours (07:00 to 18:59 UTC here), every 15 minutes while active, hourly otherwise. 60 jobs a day, 1,860 in 31 days, leaving only 140 minutes of a GitHub Free allowance.
on:
schedule:
- cron: '7,22,37,52 7-18 * * *'
- cron: '7 0-6,19-23 * * *'
workflow_dispatch:
To build your own window, choose its start hour in UTC (H, 0 to 23), its length in whole hours (A, 1 to 23) and its active interval (I, 20 or 15 minutes). The active hours are H through H+A-1, continuing from 23 to 0, and every other hour is outside the window. The active line uses minutes 7,27,47 at 20 minutes or 7,22,37,52 at 15 minutes; the outside line uses minute 7. Then check its Actions usage against your plan's allowance in GitHub Actions minutes:
jobs/day = active hours × (60 / active interval) + outside-window hours
monthly estimate = jobs/day × 31, at one billed minute per job
minutes left = allowance − monthly estimate
- Above your allowance: do not use it unless you have deliberately set up paid usage beyond the allowance
- Fewer than 140 minutes left: not recommended
- 140 to 263 minutes left: lower headroom, like the Extended and Fast hybrid profiles
- 264 minutes or more left: acceptable under this calculation, but the minutes left must still cover Sync Now runs, failed runs and other workflows
- GitHub Free: 20 minutes is the shortest safe active interval for a 16- or 18-hour window with hourly checks outside it
- 13 active hours at 15 minutes gives 1,953 jobs and leaves only 47 minutes. It is not recommended
- 16 active hours at 15 minutes gives 2,232 jobs, and 18 hours at 15 minutes gives 2,418 jobs. Both exceed GitHub Free
- Faster or longer windows need Pro, Team or paid usage, or a shorter active window
- Every hour from 0 to 23 must appear in exactly one of the two lines, and
workflow_dispatch:must stay - A window that crosses midnight uses split hour ranges, never a wrap-around range such as
14-5. For example, an active window from 14:00 to 05:59 UTC at 20 minutes uses'7,27,47 0-5,14-23 * * *'and'7 6-13 * * *' - A UTC schedule has no daylight-saving transitions, so the 31-day estimate applies all year
How the Workflow Works
The workflow file is the authority for this behavior. In outline:
- Actions: GitHub's checkout, setup-python and upload-artifact actions are pinned to full commit SHAs of their current Node 24 releases, with the release version in a comment
- Permissions: the workflow requests only
contents: write - One sync at a time: runs share one concurrency group and queue instead of cancelling each other. After a GitHub outage, several queued runs may execute one after another, and each counts as at least one billed minute
- Time limit: a run is stopped after 15 minutes. A normal sync finishes in well under a minute, but a first sync that builds
history.jsoncan take longer; if a first run is stopped at the limit, raisetimeout-minutesin the workflow - Default branch only: a manual run started from another branch stops before any secret is used or anything is synced
- Credential masking:
sync.pyprints the first five characters of your athlete ID and API key; the workflow registers both with GitHub's log masking beforesync.pyruns - Output checks: the run fails if
latest.jsonis missing, is not valid JSON, or was not rewritten by this sync, or if any other generated file is not valid JSON - Artifact first: a manual run uploads the training-data artifact before anything is committed, so a later publishing problem does not remove your download
- One commit: the generated files, the daily archive file and the README timestamp are committed together and pushed once, on top of the latest state of your default branch. Your own changes to other files, including the README, are kept
- Generated files changed during the run: if a generated file changed in the repository after the run started, for example because you deleted
history.json, the run fails without publishing and the next run syncs again - Push outcome: if a push does not report success, the workflow reads the branch back before doing anything else:
- if this run's commit is on the branch, the run succeeds
- if it is confirmed absent, the workflow retries, up to three attempts in total, and then fails with a message that no commit from this run is on the branch
- if the branch cannot be read back, or the check cannot be completed, the run stops with OUTCOME UNKNOWN (exit code 2) and does not retry, because the commit may or may not have landed
Archive
- Each successful run copies
latest.jsontoarchive/YYYY-MM/YYYY-MM-DD.json, using the UTC date - Later successful runs on the same UTC day overwrite that file, so the current tree gains at most one archive file per day
- This limits the number of files in the current tree; it does not shrink Git history, which keeps every committed version
- Timestamped archive files written by earlier versions of the workflow (
YYYYMMDD_HHMMSS.json) are left untouched. You can delete them in a normal commit if you want a smaller current tree, for example for platforms with file-count import limits. That removes them from the current tree only; they remain in Git history - Rewriting Git history to remove old files is destructive and is not covered by this guide
FTP History Tracking
The sync script automatically creates and maintains ftp_history.json to track FTP changes over time. This enables Benchmark Index calculation.
{
"indoor": {
"2025-10-15": 255,
"2025-11-03": 260,
"2025-12-01": 265
},
"outdoor": {
"2025-10-15": 265,
"2025-11-03": 270,
"2025-12-01": 278
}
}
- Created automatically on first run of
sync.py - New entries added only when FTP changes (not on every run)
- Indoor and outdoor FTP tracked separately
- The file may be rewritten on every sync without any content change; Git only records a change when an FTP value changes
- GitHub Actions workflow commits updates automatically
Benchmark Index
The Benchmark Index measures FTP progression over 8 weeks:
Benchmark Index = (Current FTP - FTP 8 weeks ago) / FTP 8 weeks ago
| Value | Interpretation |
|---|---|
| +5% | Strong progression |
| +2% to +5% | Normal build phase gains |
| 0% to +2% | Maintenance |
| -2% to 0% | Minor regression (may be normal in recovery) |
| < -2% | Significant regression. Investigate |
Note: Requires ~8 weeks of data before Benchmark Index becomes available.
Usage with AI
Once set up, configure your AI platform using the instructions in the main README.
Private repository with a GitHub connector or authenticated repository access (recommended): If your AI platform has a GitHub connector, connect your data repo directly and ask the AI to read the files by path: latest.json, history.json, intervals.json, routes.json and saved_workouts.json, plus ftp_history.json when needed. It can read any other committed files the same way. No raw URLs are needed. Whether the AI sees new commits automatically, or needs you to refresh, sync or re-import, depends on the platform; see the connector table. If you commit DOSSIER.md and SECTION_11.md to the repo as well, one connection covers your data, your dossier and the protocol. Check what the connector is actually scoped to. Where it exposes only the JSON, supply SECTION_11.md and the dossier separately.
A GitHub connector is normally read-only. Where its write access is unavailable or unverified, your AI cannot update DOSSIER.md in place: it returns the revised dossier as an artifact and says plainly that the source was not updated. You then commit that file over the official DOSSIER.md, or replace the official copy with it, as would a repository writer whose access you have verified separately. Nothing in the automated pipeline does this for you. The sync workflow commits only the generated data files and the repository README; it never touches your dossier.
Public repository with URL fetch: Use this only if your data repository is public. Everything committed to it, including your training data, is then readable by anyone (see Privacy Notes). Provide these URLs to your AI coach:
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/latest.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/history.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/ftp_history.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/intervals.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/routes.json
https://raw.githubusercontent.com/[your-username]/[repo-name]/main/saved_workouts.json
Private repository without a connector: Run Sync Now, download the training-data artifact from the run page, and upload the JSON files to your AI chat, or use another delivery path that supplies GitHub authentication. Plain raw URLs will not work for a private repository.
latest.json has the current 7-day snapshot, history.json provides longitudinal context for trend analysis, intervals.json has per-interval detail for recent structured sessions, routes.json has route/terrain data for planned events with GPX/TCX attachments (its events list is empty when there are none), and saved_workouts.json mirrors your Intervals.icu saved workouts.
Troubleshooting
Workflow fails with "secret not set"
- Check secret names match exactly:
ATHLETE_ID,INTERVALS_KEY - Secrets are case-sensitive
Manual run stops at "Verify default branch"
- The run was started from a branch other than your default branch
- Start it again from Run workflow with the default branch selected
Run fails with "latest.json was not updated by this sync"
sync.pyfinished without writinglatest.json- Check that the
python sync.pystep in your workflow still passes--output latest.json(see Customization) and that the step'senv:block still setsATHLETE_IDandINTERVALS_KEY
Run fails with "Generated data changed in the repository during this run"
- A generated file was changed, added or deleted in the repository while the run was in progress
- Nothing from that run was published; the next run syncs again from the updated repository
Run fails with "The push was not accepted after 3 attempts"
- Read-back confirmed that no commit from this run is on the branch
- Check the log for a permission error (see Step 4), then run the workflow again
Run stops with "OUTCOME UNKNOWN"
- A push did not report success and the workflow could not confirm whether it landed, so it did not retry
- The log names each commit this run tried to push, in its
Pushing commitlines and again in the OUTCOME UNKNOWN message. Look for those commits anywhere in your default branch's commit history, not only as the latest commit: later syncs may already have landed on top of it, and another sync's commit with the same title is not this run's - If one of this run's commits is there, the data was published. If you have confirmed that none is there, run the workflow again
- Until you can tell, the outcome stays unknown: it does not mean the run failed, and it is not a reason to start runs again blindly
Scheduled runs are late or missing
- GitHub runs schedules best-effort and can delay or drop them; use Sync Now when you need fresh data
- Check your Actions minutes (see GitHub Actions minutes); runs stop or incur charges once the allowance is used, depending on your settings
- In a public repository, GitHub disables scheduled workflows after 60 days without repository activity
No data in latest.json
- Verify your Intervals.icu API key is valid
- Check you have activities in the last 7 days
- Look at workflow logs for specific errors
ftp_history.json not updating on GitHub
- The file only changes when your indoor or outdoor FTP changes
- Use the current
auto-sync.yml, which commits it together with the other generated files - Check workflow logs for git errors
history.json not generated
- History generates automatically on first run, then regenerates when outdated
- Scheduled runs refresh an outdated
history.jsononly when the run's number is a multiple of 16. Other scheduled runs skip the refresh, sync everything else normally and say so in the log. The refresh can therefore arrive several hours after the file becomes outdated, and later if those runs are delayed, cancelled or fail - Sync Now skips only that limit. It refreshes an outdated file unless an earlier failed refresh is still waiting out its retry delay, and it does nothing to a file that is not outdated
- Delete
history.jsonfrom your repo and re-run to force regeneration. Do this while no sync is running; a run already in progress fails without publishing and the next run regenerates it - Check workflow logs. History generation is non-critical and won't fail the sync
404 error on JSON URL
- Ensure
latest.jsonexists in repo root; check it in the repository's Code view - Private repository: a plain
raw.githubusercontent.comURL normally returns 404 when the request carries no GitHub authentication. This is expected, and being signed in to github.com in your browser does not make such a URL a reliable check. Use the Code view to check files, and a GitHub connector or the training-data artifact to give them to your AI (see Usage with AI) - Public repository: verify the URL format (use
mainnotmaster) - For private repo access from AI platforms, see the main README troubleshooting
For general AI platform issues (data not fetching, AI fabricating metrics, connector problems), see the main README troubleshooting guide.
Customization
Change sync frequency:
Edit the schedule block in auto-sync.yml. For Default, Fast mode and hybrid schedules and their Actions usage, see Sync Schedule and GitHub Actions Usage. For other schedules:
- Avoid minute
0, the busiest time for GitHub scheduled workflows - Every hour:
7 * * * * - Every 6 hours:
7 */6 * * * - Keep
workflow_dispatch:so Sync Now keeps working, and check the monthly job count before committing
Change data range:
In the Run sync step, change only the --days value and keep the rest of the command:
python sync.py \
--athlete-id "$ATHLETE_ID" \
--intervals-key "$INTERVALS_KEY" \
--days 14 \
--output latest.json || {
echo "❌ Sync script failed with exit code $?"
exit 1
}
Keep --output latest.json: without it, sync.py does not write latest.json and the run fails at the output check.
Privacy Notes
The script does not anonymize your data. Only metadata.athlete_id is redacted; activity names, date of birth, sex, height, location, timezone, athlete notes, and route coordinates are passed through, and saved_workouts.json carries your saved workouts in full: their names, descriptions, folder names and complete structures, which reveal planning intent such as goal events, target adaptations and prescribed intensities. Publishing the repo publishes all of it. See Privacy & Security.
Activity and event IDs are always real (opaque database keys, not PII) to enable features like coach annotations and planned-vs-actual pairing. Indoor/virtual ride names are preserved for workout identification.
For additional privacy, use a private repository and a separate GitHub account for your data repository.
GitHub Actions billing and repository visibility are separate questions. Billing concerns the runner minutes your workflows use. Visibility decides who can read your committed training data: a public repository does not use standard-runner minutes, but it publishes that data. The training-data artifact from a manual run stays behind your repository's access controls while GitHub stores it; for a public repository that means any signed-in GitHub user. Once you download the artifact or upload its files to an AI platform, that copy is handled under the recipient platform's terms and retention. Section 11 does not operate a hosted backend; your data moves only between Intervals.icu, GitHub and the services you choose.
Files Reference
| File | Purpose | Auto-created |
|---|---|---|
latest.json |
Current 7-day training data for AI consumption | Yes |
history.json |
Longitudinal data: daily (90d), weekly (180d), monthly (3y) | Yes |
intervals.json |
Per-interval segment data for recent structured sessions | Every sync (activity list may be empty) |
routes.json |
Route/terrain data for planned events with GPX/TCX attachments | Every sync (events may be []) |
saved_workouts.json |
Read-only mirror of your Intervals.icu saved workouts | Every sync (own 6h refresh throttle) |
ftp_history.json |
FTP progression tracking for Benchmark Index | Yes |
archive/ |
Daily snapshots of latest.json (archive/YYYY-MM/YYYY-MM-DD.json, UTC date, overwritten during the day) |
Yes |
Update Notifications
This setup does not notify you of upstream updates. Two separate mechanisms exist, and neither reaches you here. The one that opens a GitHub Issue runs only when sync.py publishes to GitHub itself, using a token and repository you have configured; this workflow deliberately avoids that path, invoking sync.py in output-only mode so the workflow does the committing. The other is a local notice that prints a single line during a sync run and creates no GitHub Issue or other notification artifact; it looks for a section11/ directory beside your data, and a data mirror built from this guide has none. Nothing will appear in your Issues tab.
Staying current is manual, and the three files you copied in Step 2 are not updated the same way. Watching the Section 11 repository will tell you that something changed, but it is not the update itself.
sync.pyis a verbatim copy. Compare it against the hash in the repository'smanifest.jsonand replace it outright when it differs..github/workflows/auto-sync.ymlmatches the published hash only while you have not changed it. If you changed its schedule or any other line, a hash mismatch is expected. When the template changes, do not replace your copy blindly: follow Updating an Existing Repository so you keep the customizations you want.README.mdis not comparable that way. You replaced its placeholders during setup, and the workflow rewrites its Last successful sync line on every run, so it will always differ from the template. Read what changed inDATA_REPO_README_TEMPLATE.mdand merge anything that applies into your own README by hand. Do not overwrite it from the template, and do not read a hash mismatch as an update.
For local setups (non-GitHub), see json-local-sync for the local update mechanism.
Updating an Existing Repository
Use this sequence when the workflow template changes, including the change from the earlier 15-minute workflow to the current one.
Manual-only setup? If you removed the
scheduleblock so the workflow runs only when you trigger it, keep that choice: after replacing the workflow in step 3, removeschedule:again and keepworkflow_dispatch:. See On-Demand Sync.
- Record your current setup. Note the
scheduleblock in.github/workflows/auto-sync.ymland any other lines you changed, such as--days. If you used the earlier*/15 * * * *schedule, you were effectively running Fast mode; check GitHub Actions minutes before keeping that frequency. - Choose a schedule: Default, Fast mode or a hybrid. See Sync Schedule and GitHub Actions Usage.
- Replace the workflow. Replace the contents of
.github/workflows/auto-sync.ymlwith the currentexamples/json-auto-sync/auto-sync.yml. Keep the filename: your README badge and Sync Now link depend on it. - Reapply only what you still want. Edit the
scheduleblock for the schedule you chose, and reapply other recorded changes you still need. Do not copy back the old publishing steps. - Keep your README. Do not replace a customized
README.mdwith the template. Keep its status badge line, its Sync Now link and its**Last successful sync:**line; merge any template wording you want by hand. - Keep your secrets.
ATHLETE_ID,INTERVALS_KEY,WEEK_STARTandZONE_PREFERENCEkeep the same names; nothing needs to change. - Run Sync Now from the default branch and check that the run is green, that exactly one new
Sync training datacommit contains the updated data files, today's archive file andREADME.md, that the training-data artifact is on the run page, and that the README timestamp changed. - Watch the next scheduled run to confirm the new schedule starts.
- Optional: delete old timestamped files under
archive/in a normal commit, as described in Archive. This changes the current tree only.
Rollback: restore the previous .github/workflows/auto-sync.yml from your repository's commit history (for example by reverting the commit that replaced it). Your data files, secrets and README are not affected. Archive files written by either version can stay side by side.
sync.py updates are separate from workflow updates; follow Update Notifications for them.