Backup, export & moving Macs
Export as JSON or CSV, schedule a daily backup, import an export with per-item conflict resolution, and move Pulsaria to a new Mac.
Everything Pulsaria knows lives in plain files on your Mac, so a backup is a file you can hold. This page covers the two export formats, the scheduled daily backup, how an import merges into data that already exists, and the exact sequence for moving to another machine.
All of it sits in one place: the sidebar entry App Settings, cards Export data and Daily auto-export.
Manual export
The Export data card has three buttons: Export JSON, Export CSV and Import JSON. Both export buttons open a native Save dialog with the file name already filled in, and both keep working when your trial has expired — getting your data out is never blocked. Only Import JSON needs an active trial or a licence.
| Button | Default file name | Contains |
|---|---|---|
| Export JSON | pulsaria-export-2026-07-25.json | Everything, and the only file Import JSON reads back |
| Export CSV | pulsaria-entries-2026-07-25.csv | One row per measurement, nothing else |
The date in the name is your local calendar day. After saving you get a toast titled JSON export saved (or CSV export saved) with the path underneath, plus a line under the buttons reading Export saved: <path> for about four seconds.
What the JSON file contains
Pretty-printed JSON, 2-space indentation. The top level:
| Key | What it holds |
|---|---|
exportedAt | ISO timestamp of the moment you exported |
version | Always 1 |
projects | All projects, complete |
kpiDefinitions | All KPI definitions, complete |
entriesByDate | Object keyed by YYYY-MM-DD, dates ascending — every entry of that day, sorted by createdAt ascending |
alerts | The stored status alerts (at most 100) |
fetchLog | The fetch log ring buffer (at most 500 records) |
connections | Connector connections as metadata: connector id, name, parameters and the Keychain referenceerence credentialRef |
entriesByDate is the full history, not a rollup. If a KPI was written three times on one day, all three rows are in the file with their timestamps.
What the export deliberately leaves out
- Connector credentials. API keys, tokens and Google Service Account JSON keys live in the macOS Keychain and never enter the file. The export carries only the reference name
credentialRef, which is worthless without the Keychain entry behind it. - App settings. Theme, language, data directory, reminder settings, auto-export settings, dismissed hints and onboarding flags all live in
config.json, which is not part of the payload. - Your licence.
license.jsonis not exported. You enter the key again on the new machine.
There is also no way to export a subset — no date range, no single project, no single KPI. Every export is the complete dump.
One exception is worth knowing before you hand an export to anyone: a token you typed into a header row of a custom HTTP KPI is stored as plain text inside the KPI definition, not in the Keychain, so it is in the JSON file. See Custom HTTP sources.
The CSV file
The CSV is for spreadsheets, pivot tables and handing your numbers to something else. It contains only entries — no projects, no targets, no alerts, no connections, no fetch log.
kpiId,kpiName,projectName,date,value,createdAt
- One row per stored entry. A KPI updated three times on one day produces three rows.
- Rows are grouped by ascending date, and within a day by ascending
createdAt. - KPI name and project name are resolved for you. A company-wide KPI has an empty
projectName; an entry whose KPI has since been deleted keeps itskpiIdbut has an emptykpiNameandprojectName. - Values containing a comma, a quote or a line break are wrapped in double quotes, with inner quotes doubled.
- The file starts with a UTF-8 BOM and uses CRLF line endings, so Excel gets umlauts right without an import wizard. There is no newline after the last row.
CSV is export-only. There is no CSV import.
Daily auto-export
The Daily auto-export card writes a dated backup once a day without you doing anything. Turn on the switch Automatic daily export; the remaining controls appear underneath it.
| Control | Default | Notes |
|---|---|---|
| Time | 18:00 | Local wall-clock time, HH:MM |
| Format | JSON + CSV | Alternatives: JSON (full backup), CSV (spreadsheet / LLM) |
| Location | <app data>/pulsaria/daily-export | Read-only field plus Choose…; a Reset button appears once you set a custom folder |
The default folder is ~/Library/Application Support/com.onepixelahead.pulsaria/pulsaria/daily-export. Files are named exactly like the manual ones — pulsaria-export-<YYYY-MM-DD>.json and pulsaria-entries-<YYYY-MM-DD>.csv — and their contents are identical.
When it runs
The scheduler wakes up every 60 seconds while Pulsaria is open. It runs the export once per local calendar day, at or after the time you configured. If your Mac was asleep or the app was closed at 18:00, the run happens on the first tick after you open Pulsaria again that day — but it is not made up on a later day. Before writing, it refreshes your automatic KPIs so the snapshot is current; if that refresh fails, the backup is still written with the data that exists.
Two conditions apply: Pulsaria must be open — there is no background helper, see Schedules & refresh — and the scheduled run needs an active trial or a valid licence.
Status and running it by hand
At the bottom of the card sit a status line and the Export now button:
- No export yet — never run.
- Last export 2 hours ago — the last success, as a relative time.
- Last export failed — check the location — hover it to read the actual error message.
Export now ignores the clock and the once-per-day rule and runs the same routine immediately, KPI refresh included. It reports the written file paths in a toast. Use it once after changing the location, to confirm the folder is really writable.
Location matters more than it looks. The card warns that other locations may only work while the app is open — the honest version is: a folder you selected through the card’s Choose… picker keeps its permission, because Pulsaria restores dialog-granted folders at launch. A path typed into a field never gets one, and the unattended run then fails against it. After changing the location, hit Export now once: if it writes the files, the scheduled run will too. If you want your backups somewhere Pulsaria never touches, the simplest pattern is to leave the default folder and let Time Machine or a sync client copy it.
Two more things to expect: a file for the same day is overwritten, and nothing is ever deleted. One JSON and one CSV per day accumulate indefinitely — prune them yourself if that matters. And there is no notification on failure; the status line in this card is the only signal.
Importing a JSON export
Import JSON reads a Pulsaria JSON export back in and merges it. It never wipes anything: new items are added, and for every item that already exists you decide individually.
The button is disabled without an active trial or licence, with the tooltip Requires a license or active trial.
Step 1 — what would be added
Pick a .json file. Pulsaria parses and analyses it without writing anything, then opens the Import dialog. Under Will be added you see one row each for KPIs, Connections, Entries, Projects and Alerts — but only the ones with a count above zero. If the file holds nothing new and only items that clash, that area reads Nothing new to add — only conflicts to resolve.
If there is neither anything new nor a conflict, no dialog opens at all. You get the toast Nothing new to import — everything in the file already exists.
When items clash, an amber note tells you how many, and the primary button becomes Resolve conflicts (n). Without conflicts it is simply Import.
Step 2 — one conflict at a time
An item conflicts when its internal id already exists locally. The dialog walks you through them, KPIs first, then connections, labelled Conflict 1 of 4. Each is shown as two columns:
| Column | For a KPI | For a connection |
|---|---|---|
| Your version | Name, Target (with unit), Source | Name, Connector, Parameters |
| Imported version | the same three fields, from the file | the same three fields, from the file |
Three buttons:
- Keep local — your version stays untouched.
- Replace — the imported version overwrites yours.
- Keep all remaining — everything still undecided stays local, and the import runs right away.
The import executes after the last decision. You then get Import complete with the per-type counts and, if anything was overwritten, a second toast: n existing item(s) replaced with the imported version.
How each kind of data is merged
| Data | Matched by | Behaviour |
|---|---|---|
| Projects | id | New ones are added. An existing project always keeps the local version — there is no keep/replace choice for projects. |
| KPIs | id | New ones are added. Conflicts default to keep-local, overwritten only via Replace. |
| Connections | id | Same policy as KPIs. |
| Entries | kpiId + createdAt, per day | Append-only and deduplicated, so re-importing the same file adds nothing. |
| Alerts | id | New ones merged in, sorted newest first, capped at 100. |
| Fetch log | — | Exported, but never imported. It is diagnostic data. |
Two things are stripped on the way in, on purpose:
- Every imported KPI loses its approval. A Bash or HTTP KPI that was approved on the old Mac arrives unapproved, and the scheduler skips it until you have looked at the actual command or URL and approved it again. That stops an export file from smuggling in a command that would run unattended. Connector KPIs never had an approval to begin with.
- Every imported connection loses its verification state, so it reads as unverified until you re-enter the secret and test it.
Where import is strict, and where it is quiet
The file must be a JSON object with projects, kpiDefinitions and entriesByDate; anything else is rejected with Not a Pulsaria export (expected an object) or Not a Pulsaria export (missing projects / kpiDefinitions / entriesByDate). The version field is never checked.
Individual rows that fail validation are dropped without a word. A row is dropped when:
- a KPI has no id or name, a
targetthat is not a finite number greater than 0, nodirectionofupordown, or nosourcestring, - a project’s status is not
active,completedoron-hold, - an alert is missing any of id, kpiId, kpiName, newStatus, previousStatus, value, timestamp, dismissed,
- a connection is missing id, name, connectorId, credentialRef, createdAt or parameters,
- a date key in
entriesByDateis not exactlyYYYY-MM-DD.
Pulsaria counts those dropped rows but never shows the number. In practice this only bites hand-edited files — a KPI whose target you set to
0vanishes from the import in silence. Check the counts in the Will be added list against what you expect before you confirm.
The write itself is staged. Projects, KPI definitions and connections go in as one block that is rolled back if any of the three fails. Alerts and entries are written afterwards, best-effort: a failure there leaves a consistent state but is not reported.
There is no undo for an import and no snapshot to roll back to. Run Export JSON first if you are importing into a Mac that already holds data you care about.
Reconnecting your connectors after an import
Because secrets never travel with an export, connector connections arrive as metadata only. Right after the import dialog closes, Pulsaria checks which of the connections it just wrote have no credential in this Mac’s Keychain, and opens the setup wizard for each of them in turn.
The header reads Reconnect Search Console with the sub-line Connection 1 of 3 — re-enter credentials, and an amber hint: “Credentials are never included in an export. Paste this connection’s key or token again to reactivate it on this machine.”
- The parameters — site URL, property id, region — are already filled in from the import. Check them, do not retype them.
- Paste the key or token. The “leave blank to keep the existing key” shortcut of normal editing is disabled here: a fresh secret is mandatory.
- Step through to Run test, then Save connection. Save stays disabled until the test comes back green.
- Closing the dialog skips that connection and immediately opens the next one.
Skipping is a one-way door. There is no reminder and no way to resume the walkthrough — you would fix it later under App Settings → Connections with the pencil icon. And a connection that never had a successful verification is drawn with a green check in the connection list rather than an amber warning, so a skipped one does not look broken until a fetch fails. See Connectors.
Connections whose connector this version does not know — an export from a newer version — are dropped from the queue without a message.
Moving to a new Mac
One export file carries the data. The rest of the work is pasting your API keys again and re-approving anything that runs a command.
On the old Mac
- Open App Settings → Export data → Export JSON and save the file somewhere you can reach from the new machine: AirDrop, a USB stick, a cloud folder.
- Make a list of your connections under App Settings → Connections. For each one you will need the original secret again — the Google Service Account JSON, the Plausible key, the Polar token. Pulsaria cannot hand them over; the Keychain entries stay on the old Mac.
- Note what is not in the export: theme, language, the reminder time, whether launch-at-login was on, and your auto-export settings.
On the new Mac
- Install Pulsaria and finish the first launch — see Install & first launch.
- Enter your licence key, or make sure the trial is still running. Import is blocked in read-only mode; see Trial & licence.
- If you keep your data in a custom folder, point Pulsaria at it before importing — see Where your data lives.
- App Settings → Export data → Import JSON, pick the file, then Import. On a fresh install there are no conflicts, so it is a single click.
- Work through the setup wizard: paste each secret, Run test, Save connection.
- Re-approve your Bash and HTTP KPIs. Open each one, press Edit, read the command or URL, then Approve. Until you do, the scheduler skips them with “Command not approved — please confirm in KPI settings”. See Shell commands and Custom HTTP sources.
- Set the things the export did not carry: theme, language, reminder, launch-at-login and the Daily auto-export card. See Settings.
Your history comes across with its original dates — entries are keyed to the local calendar day they were recorded on, not to the day you imported them.
Moving is not the same as syncing. If you want two Macs working on the same data at the same time, that is the shared-folder setup described in Where your data lives, with its own trade-offs.
Where to go next
- Where your data lives — file layout, the data directory setting, shared folders
- Privacy: what leaves your Mac — what is sent where, and what never is
- Connectors — connections, credentials and the setup wizard
- Schedules & refresh — the 60-second scheduler the auto-export rides on